【查找算法】之二分查找

一、算法介绍

二分查找,也称为折半查找,是一种在有序数组中查找特定元素的高效算法。对于包含 n 个元素的有序数组,二分查找的步骤如下:

  • 确定搜索范围:首先,将要查找的元素与数组中间的元素进行比较。如果查找的元素等于中间元素,则找到了目标值。否则,如果目标值小于中间元素,则在左半边继续查找;如果目标值大于中间元素,则在右半边继续查找。
  • 缩小搜索范围:根据比较结果,将搜索范围缩小为剩余数组的一半,并重复执行步骤 1,直到找到目标值或确定目标值不存在。

二分查找算法的关键优势在于每一步都能将搜索范围减半这使得算法的时间复杂度为 O(log n),相比线性搜索算法的 O(n) 更加高效,尤其是对于大型数据集。

二、时间复杂度计算

二分搜索最坏的情况就是折半一直找到最后一个元素,首先观察规律

开始时,是从n个元素中查找

第一次折半时,是从 n 2 \frac{n}{2} 2n个元素中查找

第二次折半时,是从 n 4 \frac{n}{4} 4n个元素中查找

假设第k次折半后只剩一个元素,即是从 n 2 k \frac{n}{2^k} 2kn个元素中查找

n 2 k \frac{n}{2^k} 2kn= 1,即 n = 2 k 2^k 2k,由对数定义知道 k = log ⁡ 2 n \log_{2}n log2n,在计算机科学中如果没有特殊说明,默认就是以2为底,即k= log ⁡ n \log n logn

即操作k次才能找到最后一个元素,所以时间复杂度为O( log ⁡ n \log n logn)

三、Java代码示例

package com.datastructures;

/**
 * 二分查找算法
 * @author hulei
 */
public class BinarySearchExample {
    /**
     * 在有序的整型数组中使用二分查找算法来寻找指定目标值的索引。
     * @param arr 一个已排序的整型数组。
     * @param target 要在数组中查找的目标值。
     * @return 如果目标值存在于数组中,则返回其索引;如果目标值不存在,则返回-1。
     */
    public static int binarySearch(Integer[] arr, int target) {
        int left = 0; // 初始化左边界为数组的第一个元素的索引
        int right = arr.length - 1; // 初始化右边界为数组的最后一个元素的索引

        while (left <= right) { // 当左边界不大于右边界时继续循环
            int mid = left + (right - left) / 2; // 计算中间位置,避免溢出
            if (arr[mid] == target) {
                return mid; // 如果中间位置的元素等于目标值,返回其索引
            } else if (arr[mid] < target) {
                left = mid + 1; // 如果中间位置的元素小于目标值,调整左边界
            } else {
                right = mid - 1; // 如果中间位置的元素大于目标值,调整右边界
            }
        }

        return -1; // 如果没有找到目标值,返回-1
    }

    /**
     * 递归实现的二分查找。
     *
     * @param arr 有序整型数组,查找范围在此数组内。
     * @param target 目标值,我们要在数组中找到这个值的索引。
     * @param left 查找范围的左边界。
     * @param right 查找范围的右边界。
     * @return 如果找到目标值,返回其索引;如果未找到,返回-1。
     */
    public static int binarySearchRecursive(Integer[] arr, int target, int left, int right) {
        // 当左边界大于右边界时,说明已经搜索完毕但未找到目标值,返回-1
        if (left > right) {
            return -1;
        }
        // 计算中间位置,避免直接计算(left + right) / 2可能的溢出问题
        int mid = left + (right - left) / 2;

        // 如果中间位置的元素等于目标值,返回其索引
        if (arr[mid] == target) {
            return mid;
            // 如果中间位置的元素小于目标值,说明目标值可能在中间位置的右边,递归搜索右半部分
        } else if (arr[mid] < target) {
            return binarySearchRecursive(arr, target, mid + 1, right);
            // 如果中间位置的元素大于目标值,说明目标值可能在中间位置的左边,递归搜索左半部分
        } else {
            return binarySearchRecursive(arr, target, left, mid - 1);
        }
    }

    public static void main(String[] args) {
        Integer[] sortedArray = {11, 22, 33, 44, 55, 66, 77, 88, 99, 100, 111, 123};
        int targetValue = 111;
        System.out.println("while循环实现二分查找");
        int resultWithoutRecursive = binarySearch(sortedArray, targetValue);
        if (resultWithoutRecursive != -1) {
            System.out.println("元素位置索引为: " + resultWithoutRecursive);
        } else {
            System.out.println("没有发现目标元素");
        }
        System.out.println("================================================");
        System.out.println("递归实现二分查找");
        int resultRecursive = binarySearchRecursive(sortedArray, targetValue, 0, sortedArray.length - 1);
        if (resultRecursive != -1) {
            System.out.println("元素位置索引为: " + resultRecursive);
        } else {
            System.out.println("没有发现目标元素");
        }

    }
}

代码分析:
该Java函数包含在一个名为BinarySearchExample的类中,提供了两种实现二分查找算法的方法:binarySearchbinarySearchRecursive。二分查找算法用于在有序数组中查找指定目标值的索引。

1. binarySearch方法:

  • 参数:一个已排序的整型数组arr和要查找的目标值target。
  • 返回值:如果目标值存在于数组中,则返回其索引;如果目标值不存在,则返回-1。
  • 实现方式:使用循环迭代来缩小查找范围,直到找到目标值或确定目标值不存在。初始化左边界left为数组的第一个元素的索引,右边界right为数组的最后一个元素的索引。在每次循环中,计算中间位置mid,并将mid与目标值比较,根据比较结果调整左右边界的值。

2. binarySearchRecursive方法:

  • 参数:一个已排序的整型数组arr,要查找的目标值target,以及查找范围的左边界left和右边界right。
  • 返回值:如果目标值存在于数组中,则返回其索引;如果目标值不存在,则返回-1。
  • 实现方式:使用递归来实现二分查找。通过不断缩小查找范围来查找目标值。计算中间位置mid,并将mid与目标值比较,根据比较结果递归调用函数自身,传入更新后的左右边界。
    在main方法中,示例代码演示了如何使用这两种方法在有序数组中查找目标值,并输出查找结果。

比如我要在以下数组 Integer[] sortedArray = {11, 22, 33, 44, 55, 66, 77, 88, 99, 100, 111, 123}
中查找目标值为55的元素的索引位置,返回结果为4,即元素55的索引位置为4
在这里插入图片描述

四、两种方式实现二分查找的算法优劣

二分查找的循环实现递归实现都有其各自的优缺点,具体如下:

循环实现

1. 循环实现的优点:

  • 空间效率:循环实现通常不需要额外的堆栈空间,因为控制流是通过迭代进行的,不会导致函数调用栈的增长。
  • 可读性:对于一些开发者来说,循环可能更容易理解和实现,逻辑更直观。
  • 性能:在处理大量数据时,由于避免了递归调用的开销,循环实现可能更快。
    2. 循环实现的缺点:
  • 代码结构:虽然更直观,但代码可能稍微复杂一些,需要手动管理边界条件。

递归实现

1. 递归实现的优点:

  • 简洁性:递归实现的代码通常更简洁,逻辑更优雅,尤其是对于熟悉递归思维的开发者。
  • 表达力:递归直接反映了二分查找的分治思想,易于理解算法的本质。

2. 递归实现的缺点:

  • 空间效率:递归会增加栈的使用,当递归深度很大时,可能导致栈溢出。
  • 效率:由于存在函数调用开销,递归在某些情况下可能比循环慢。
  • 可读性:对不熟悉递归的人来说,递归代码可能更难理解。

在实际应用中,选择哪种实现方式通常取决于具体场景,如数据规模、性能要求、代码可读性和维护性等因素。对于小规模数据或对代码简洁性有较高要求的情况,递归可能是不错的选择。而对于大规模数据或对性能敏感的场合,循环实现可能更为合适。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/601739.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

链舞算法谱---链表经典题剖析

前言&#xff1a;探究链表算法的奥秘&#xff0c;解锁编程新世界&#xff01; 欢迎来到我的链表算法博客&#xff0c;这将是您深入了解链表算法&#xff0c;提升编程技能的绝佳机会。链表作为数据结构的重要成员之一&#xff0c;其动态性和灵活性在实现某些功能上发挥不可替代的…

联发科技发布天玑9300+旗舰5G生成式AI芯片 | 最新快讯

5 月 7 日消息&#xff0c;联发科技今天举办了天玑开发者大会 2024。大会上&#xff0c;联发科技开启了“天玑 AI 先锋计划”&#xff0c;联合业界生态企业发布了《生成式 AI 手机产业白皮书》&#xff0c;分享了生成式 AI 端侧部署的解决方案“天玑 AI 开发套件”。同时&#…

界面组件DevExpress Reporting中文教程 - 如何按条件显示页面水印?

DevExpress Reporting是.NET Framework下功能完善的报表平台&#xff0c;它附带了易于使用的Visual Studio报表设计器和丰富的报表控件集&#xff0c;包括数据透视表、图表&#xff0c;因此您可以构建无与伦比、信息清晰的报表。 从防止未经授权的使用到建立所有权和真实性&am…

java语言数据结构(单链表)

前言 不得承认java应用的广泛&#xff0c;所以毅然决定java版本的数据结构和算法专题还是要坚决更新。每日更新2题&#xff0c;希望学习的小伙伴可以关注一波跟上&#xff0c;评论区欢迎讨论交流。 实现原理 节点&#xff08;Node&#xff09;&#xff1a;链表的基本构建单元…

【Git】Git学习-07:git diff查看差异

学习视频链接&#xff1a;【GeekHour】一小时Git教程_哔哩哔哩_bilibili 默认比较工作区和暂存区的差异 工作区和版本库的内容比较 git diff HEAD / git diff --staged 暂存区和版本库内容比较 git diff --cached 比较特定两个版本的不同 git diff 版本id 版本id HEAD表示提交的…

【Git】Git学习-17:git rebase,且解决合并冲突

学习视频链接&#xff1a;【GeekHour】一小时Git教程_哔哩哔哩_bilibili​编辑https://www.bilibili.com/video/BV1HM411377j/?vd_source95dda35ac10d1ae6785cc7006f365780 理论 git rebase 目标分支&#xff1a;把当前分支的提交&#xff0c;从与目标分支的共同主祖先处断开…

Ubuntu 24.04 LTS 安装 touchegg 开启触控板多指手势

文章目录 〇、概述一、安装 touchegg二、安装 gnome-shell 扩展 X11 Gestures三、安装可视化配置工具 touche 〇、概述 之前为了让笔记本支持多指手势&#xff0c;我安装的是 fusuma&#xff0c;安装教程详见 这篇文章 &#xff0c;考虑到 fusuma 安装过程繁琐且不支持可视化配…

Redis单机安装

1.编译 cd redis安装目录 makemake install2.修改配置文件redis.conf #端口修改 port 6379 #后台进程启动 yes daemonize yes # daemonize no #注释掉 为了可以远程连接 #bind 127.0.0.1 #设置密码 requirepass pwd3.启动 ./redis-server ../redis.conf查看进程 [rootlocal…

地盘紧固的关键技术——SunTorque智能扭矩系统

底盘紧固件是汽车底盘系统中不可或缺的一部分&#xff0c;它们负责连接和固定各个部件&#xff0c;确保车辆行驶的安全和稳定。底盘紧固件的开发涉及到多个环节和关键技术&#xff0c;下面SunTorque智能扭矩系统将详细介绍底盘紧固件开发流程和关键技术。 一、底盘紧固件开发的…

舞台演出因全息纱幕投影有哪些新变化?

如今&#xff0c;随着时代的演进&#xff0c;我们对舞台体验的追求愈发深入&#xff0c;渴望在这片艺术天地中&#xff0c;感受到超越日常的震撼与感动&#xff0c;而这种追求&#xff0c;也正与现代技术的飞速发展不谋而合。其中&#xff0c;全息纱幕投影技术以其独特的魅力&a…

Mysql 基础 order by ,as ,limit,case,asc、desc

order by 、as 、limit 、asc&#xff08;ascending 正序 默认&#xff09; desc&#xff08;descending 倒序&#xff09; SELECT 学号, 姓名, 成绩, CASEWHEN 成绩 > 90 THEN 优秀WHEN 成绩 > 80 THEN 良好WHEN 成绩 > 60 THEN 及格ELSE 不及格 END as 成绩级别 FRO…

数组刷题集

文章目录 概要26. 删除有序数组中的重复项代码Python 189. 轮转数组代码Python 小结 概要 记录一些数组相关的刷题集。 数组数据结构早就写过了&#xff0c;一直没有写相关的刷题集。今天补上。 26. 删除有序数组中的重复项 leetcode26题 题目如上图&#xff1b;也可以去leet…

Vince9120雅思小作文笔记——P1 Intro(前言)

文章目录 链接P1 Intro&#xff08;前言&#xff09;字数限制题型综述&#xff08;problem types overview&#xff09;1. **柱状图&#xff08;Bar Chart&#xff09;** - 描述不同类别在某个或多个变量上的数据量比较。2. **线图&#xff08;Line Graph&#xff09;** - 展示…

【只显示某一层,其它层隐藏】- PCB板工艺及AD软件配置

在pcb文件视图下&#xff0c;按下字母L&#xff0c;弹出如下框&#xff0c; 选择view options 在single layer mode 勾选ON&#xff0c;就可以隐藏其他层 单层显示效果 方法二&#xff1a;shitfs快捷键

SH-PEG-SH,聚乙二醇二巯基广泛用于生物学应用、纳米技术和材料研究中

【试剂详情】 英文名称 SH-PEG-SH 中文名称 聚乙二醇二巯基&#xff0c;双硫醇PEG&#xff0c; 双巯基聚乙二醇&#xff0c;双巯基封端聚乙二醇 外观性状 白色固体粉末 分子量 0.4k&#xff0c;0.6k&#xff0c;1k&#xff0c;2k&#xff0c;3.4k&#xff0c;5k&#x…

前端开发攻略---手写bind函数,看这篇就够了。

1、演示 2、bind函数介绍 在JavaScript中&#xff0c;bind() 函数用于创建一个新函数&#xff0c;其 this 关键字设置为提供的值&#xff0c;而不是调用时绑定的值。这对于在事件处理程序中绑定函数的上下文或者在类中绑定方法的上下文非常有用。 bind() 方法的语法如下&#x…

LeetCode //C - 85. Maximal Rectangle

85. Maximal Rectangle Given a rows x cols binary matrix filled with 0’s and 1’s, find the largest rectangle containing only 1’s and return its area. Example 1: Input: matrix [[“1”,“0”,“1”,“0”,“0”],[“1”,“0”,“1”,“1”,“1”],[“1”,“…

怎样通过PHP语言实现远程控制一路开关

怎样通过PHP语言实现远程控制一路开关呢&#xff1f; 本文描述了使用PHP语言调用HTTP接口&#xff0c;实现控制一路开关&#xff0c;一路开关可控制一路照明、排风扇等电器。 可选用产品&#xff1a;可根据实际场景需求&#xff0c;选择对应的规格 序号设备名称厂商1智能WiFi…

全面升级!一套基于Spring Boot 3+JDK17的实战项目!

最近把mall项目升级支持了Spring Boot 3JDK17&#xff0c;今天就来介绍下mall项目做了哪些升级&#xff0c;包括依赖的升级、框架的用法升级以及运行部署的改动&#xff0c;目前Spring Boot 3版本代码在mall项目的dev-v3分支下&#xff0c;希望对大家有所帮助&#xff01; mall…

压力测试-JMeter常用插件、服务器硬件监控

1.写在前面 在前一篇文章中&#xff0c;我们已经对jmeter有了一个入门的学习。 掌握了JMeter安装、入门、结果分析等内容&#xff0c;详情可查看这里&#xff1a;压力测试-JMeter安装、入门、结果分析 对于jmeter默认的插件&#xff0c;往往不太够&#xff0c;例如&#xff…