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.​比较与判断​​:

  • 如果arrmid == target,查找成功,返回mid
  • 如果arrmid < target,说明目标在右侧,更新left = mid + 1 (此时mid值一定不是目标值,那么接下来要查找的左区间结束下标位置就是 middle + 1,遵循区间左闭右闭)
  • 如果arrmid > 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.​比较与判断​​:

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

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) / 2 或 mid = 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/

相关推荐
youdexiang15 小时前
Android录音软件时间关键词检索功能分析
java·人工智能
sunshine22 girl16 小时前
Java学习七 Java 项目实战1-项目创建和主页面搭建
java·开发语言·学习
code2cat16 小时前
Java进阶篇之AtomicStampedReference:引用回到原值,版本戳仍能记住变化
java·cas·并发编程·aba
念何架构之路17 小时前
zap采样器与性能优化内幕
算法·性能优化·哈希算法
泡茶喝茶写代码17 小时前
A股量化数据工程:从 REST 接口到策略信号(第 8 篇):成长能力因子:营收与利润增速
java·python·股票数据api·股票数据api接口·股票量化数据api·股票量化数据接口·股票数据api数据
晒太羊的猫18 小时前
hot100动态规划刷题
算法·动态规划
\光辉岁月/18 小时前
7.javase-面向对象
java·开发语言
weixin_5310946918 小时前
heap_4内存管理
java·开发语言·算法
余额瞒着我当琳18 小时前
红黑树的实现--红黑树的概念、规则、效率、结构、旋转、查找、验证
数据结构·c++·算法
程序员南飞18 小时前
win系统安装minio
java·数据结构·算法