LeetCode-704-二分查找

题目链接

题目描述

给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果 target 存在返回下标,否则返回 -1

你必须编写一个具有 O(log n) 时间复杂度的算法。

示例1:

复制代码
输入: nums = [-1,0,3,5,9,12], target = 9
输出: 4
解释: 9 出现在 nums 中并且下标为 4

示例2:

复制代码
输入: nums = [-1,0,3,5,9,12], target = 9
输出: 4
解释: 9 出现在 nums 中并且下标为 4

解题思路

将该题简化,我们会发现以下几个关键词:有序数组、无重复元素、O(log n),这是一道很典型的二分查找,借由该题,我们将详细叙述二分的思想。

1. 二分查找的基本原理

二分查找(Binary Search)是一种基于分治策略的查找算法,其核心思想是通过不断将有序数据集对半分割来快速定位目标元素

。算法要求数据必须预先排序(通常为升序或降序),从而利用有序性快速缩小搜索范围。

数学基础 ​​:对于一个长度为n的有序数组,二分查找最多需要⌊log₂n⌋+1次比较

。例如,当n=1000时,最多只需10次比较即可完成查找,而线性查找可能需要1000次比较。这种指数级的效率提升使得二分查找在处理大规模数据时极具优势。

适用场景​​:

  • 数据结构支持随机访问(如数组、向量)
  • 数据必须有序(若无序需先排序)
  • 存在明确的判断条件(能确定目标在左半区还是右半区)

2. 二分查找的两种主要实现方式

2.1 左闭右闭区间 [left, right]

这是最直观的实现方式,搜索区间包含左右边界指向的元素。

​​算法流程​​:

​1.​初始化边界​​:设置左边界left = 0,右边界right = nums.length - 1

​2.​循环条件​​:当left ≤ right时继续查找(确保搜索区间有效),因为left == right是有意义的

​3. ​计算中间位置​​:mid = left + ((right - left)>>1)(防止整数溢出的安全写法,还涉及到符号运算顺序级)

​4.​比较与判断​​:

  • 如果arr[mid] == target,查找成功,返回mid
  • 如果arr[mid] < target,说明目标在右侧,更新left = mid + 1 (此时mid值一定不是目标值,那么接下来要查找的左区间结束下标位置就是 middle + 1,遵循区间左闭右闭)
  • 如果arr[mid] > target,说明目标在左侧,更新right = mid - 1

​​5.终止条件​​:如果循环结束仍未找到目标,返回-1表示查找失败

代码实现

java 复制代码
class Solution {
    public int search(int[] nums, int target) {
        int left = 0;
        int right = nums.length - 1;
        while(left <= right){
            int mid = left + ((right - left) >> 1); // 防止溢出
            if(nums[mid] == target)
                return mid;
            else if (nums[mid] < target)
                left =  mid + 1;
            else 
                right = mid - 1;
        }
        return -1;
    }
}

2.2 左闭右开区间 [left, right)

这种实现方式中,搜索区间包含左边界指向的元素,但不包含右边界指向的元素。

​​算法流程​​:

1.​初始化边界​​:设置left = 0,right = nums.length(右边界不包含)

2.循环条件​​:当left < right时继续查找(这里使用 < ,因为left == right在区间[left, right)是没有意义的)

3.​​计算中间位置

4.​比较与判断​​:

  • 如果arr[mid] == target,查找成功,返回mid
  • 如果arr[mid] < target,更新left = mid + 1
  • 如果arr[mid] > target,更新right = mid(注意不是mid-1),因为当前arr[mid]不等于target,去左区间继续寻找,而寻找区间是左闭右开区间,所以right更新为mid,即:下一个查询区间不会去比较arr[mid]

5.终止条件​​:循环结束时仍未找到目标,返回-1

​​代码实现​​:

java 复制代码
class Solution{
    public int search(int [] nums, int target){
        int left = 0;
        int right = nums.length;
        //有问题 int right = nums.length - 1; 
        while (left < right){
            int mid = left + ((right  - left) >> 1);
            if(nums[mid] == target)
                return mid;
            else if (nums[mid] < target)
                left = mid + 1;
            else 
                right = mid;
        }
        return -1;
    }
}

2.3 两种实现方法对比

特性 左闭右闭区间 [left, right] 左闭右开区间 [left, right)
初始右边界 right = nums.length - 1 right = nums.length
循环条件 while (left <= right) while (left < right)
边界更新 (nums[mid] > target) right = mid - 1 right = mid
区间为空的条件 left > right left == right
mid 计算偏好(防死循环) mid = left + (right - left) / 2mid = left + (right - left + 1) / 2 均可 必须使用 mid = left + (right - left) / 2(偏左)

3. 关键点总结

3.1 防止整数溢出

计算中间位置时,应使用left + (right - left) / 2而非(left + right) / 2,这样可以避免当left和right都很大时相加导致的整数溢出。

3.2 循环条件的选择

循环条件必须与区间定义保持一致。左闭右闭区间使用left <= right,因为当left等于right时区间仍包含一个元素;左闭右开区间使用left < right,因为当left等于right时区间为空。

3.3 边界更新的逻辑

边界更新必须确保每次迭代都能缩小搜索范围,同时不遗漏可能的目标元素。特别是在左闭右开区间中,当目标值小于中间值时,右边界更新为mid而非mid-1,因为右边界本身不指向待检查元素。

更多力扣算法解析,尽在https://saberanakin.github.io/

相关推荐
百锦再23 分钟前
第8章 模块系统
android·java·开发语言·python·ai·rust·go
没有bug.的程序员27 分钟前
Eureka 注册中心原理与服务注册发现机制
java·spring·云原生·eureka·架构·注册中心·服务注册发现
optimistic_chen28 分钟前
【Java EE进阶 --- SpringBoot】统一功能处理
java·spring boot·java-ee·json·统一功能处理
m0_5913389130 分钟前
day8鹏哥C语言--函数
c语言·开发语言·算法
_OP_CHEN34 分钟前
算法基础篇:(二)基础算法之高精度:突破数据极限
算法·acm·算法竞赛·高精度算法·oj题
一只老丸36 分钟前
HOT100题打卡第30天——技巧
算法
西岭千秋雪_41 分钟前
Zookeeper数据结构
java·数据结构·分布式·zookeeper
青云交42 分钟前
Java 大视界 --Java 大数据机器学习模型在金融风险压力测试中的应用与验证
java·随机森林·机器学习·lstm·压力测试·联邦学习·金融风险
程序编程- Java1 小时前
和平精英java 游戏程序
java·游戏程序·安全架构·玩游戏
Bi_BIT1 小时前
代码随想录训练营打卡Day38| 动态规划part06
算法·动态规划