二分查找细节理解

要理解二分查找中 while(l < r)while(l <= r) 的区别,核心在于二分的 "目标" 和 "边界收缩逻辑" ------ 前者是 "收敛到唯一解",后者是 "在区间内验证目标是否存在"。下面结合你给出的三个经典案例,拆解背后的核心逻辑:

先明确核心结论

循环条件 核心目标 边界收缩特点 适用场景
l < r lr 收敛到同一个索引(唯一解) 收缩时永不跳过可能的解(r=mid 找 "唯一值"(如峰值、旋转数组最小值)
l <= r 在区间内验证目标是否存在,找到则返回 收缩时可能跳过(r=mid-1/l=mid+1 找 "特定值"(如有序数组查 target)

逐个分析你的代码案例

案例 1:findPeakElement(找峰值,l < r

为什么不用 l <= r

  1. 目标是 "收敛到唯一解" :我们不需要在循环内判断 "mid 是不是峰值",而是通过趋势收缩,让 lr 最终指向同一个索引(必然是峰值)。
  2. 越界 / 死循环风险 :若用 l <= r,当 l==r 时会进入循环:
    • mid = l = r,此时判断 nums[mid] < nums[mid+1],若 mid 是数组最后一个元素,mid+1 会越界;
    • mid 不是最后一个元素,right=mid 会导致 lr 永远相等,陷入死循环。
  3. 逻辑冗余l==r 时已经找到唯一解,继续循环无意义。
案例 2:search(旋转数组查 target,l <= r
复制代码
while(l <= r) {
    int mid = l + (r - l)/2;
    if(nums[mid] == target) return mid; // 找到目标,直接返回
    // 收缩边界(跳过mid,因为mid已经验证不是target)
    if(nums[l] <= nums[mid]) {
        if(nums[mid] > target && target >= nums[l]) r = mid - 1;
        else l = mid + 1;
    } else {
        if(nums[mid] < target && target <= nums[r]) l = mid + 1;
        else r = mid - 1;
    }
}
return -1; // 区间为空,未找到

为什么必须用 l <= r

  1. 目标是 "验证存在性" :我们需要在循环内逐个验证 mid 是否是目标值,找到则返回,没找到则收缩区间。
  2. 边界收缩逻辑要求 :每次收缩都会跳过 midr=mid-1/l=mid+1),因为 mid 已经验证不是 target。若用 l < r,会漏掉最后一个可能的解:
    • 比如数组 [1],target=1,l=0, r=0l < r 不成立,直接返回 - 1,错误;
    • l <= r 会进入循环,mid=0,找到 target 返回 0,正确。
  3. 循环终止条件合理 :当 l > r 时,说明区间内所有元素都验证过,没有 target,返回 - 1 即可。
案例 3:findMin(旋转数组找最小值,l < r
复制代码
while(l < r) {
    int mid = l + (r - l)/2;
    if(nums[mid] > nums[r]) {
        l = mid + 1; // 最小值在右侧,mid不可能是解,跳过
    } else {
        r = mid;     // 最小值在左侧(含mid),mid可能是解,不跳过
    }
}
return nums[l]; // l==r,就是最小值

为什么不用 l <= r

  1. 目标是 "收敛到唯一解" :旋转数组的最小值是唯一的,我们通过比较 nums[mid]nums[r] 收缩区间,最终让 lr 指向最小值的索引。
  2. 逻辑冗余 / 死循环 :若用 l <= r,当 l==r 时进入循环,mid=l=rnums[mid] > nums[r] 不成立,r=mid 导致循环永不退出。
  3. 效率更高l < r 循环次数更少,收敛到唯一解后直接返回,无需额外判断。

通用判断法则(记住这 2 点)

什么时候用 l < r

满足以下任一条件即可:

  1. 目标是找 "唯一值"(如峰值、旋转数组最小值、有序数组找第一个 / 最后一个元素);
  2. 边界收缩时不跳过可能的解 (即有 r=mid,而非 r=mid-1);
  3. 最终返回 l(或 r (因为 l==r)。
什么时候用 l <= r

满足以下任一条件即可:

  1. 目标是 "查找特定值是否存在"(如有序数组查 target、旋转数组查 target);
  2. 边界收缩时跳过已验证的 mid (即 r=mid-1l=mid+1);
  3. 循环内有 "找到目标直接返回" 的逻辑,最终可能返回 - 1(未找到)。

补充:易混点对比

对比项 l < r l <= r
循环终止时 l == r(区间长度为 1) l > r(区间为空)
收缩方式 至少保留 mid(r=mid 必跳过 mid(r=mid-1/l=mid+1
返回值 循环外返回 l/r 循环内返回目标,循环外返回 - 1
典型场景 找唯一极值 / 边界值 找特定值(存在性验证)

总结

二分循环条件的选择,本质是匹配 "目标" 和 "边界收缩逻辑"

  • 若想让区间收敛到唯一索引(找极值 / 唯一值),用 l < r
  • 若想在区间内验证目标是否存在(找特定值),用 l <= r
cpp 复制代码
    int findMin(vector<int>& nums) {
        int l=0,r=nums.size()-1;
        
        while(l<r)//为什不是<=,因为总会出现l == r,如果是<=满足条件进入循环会一直循环下去死循环
        {//其实l == r 就确定是唯一一个值,就找到了要寻找的值
            int mid = l + (r-l)/2;
            if(nums[mid]>nums[r])
            {
                l=mid+1;
            }else{
                r=mid;//或者更直接的说法是<=的情况每次更新左右区间的时候l=mid+1,r=mid-1
            }//跳过了mid的值的判断,最后l == r的时候 mid=l=r,要进入循环才能将mid的位置的数进行判断
        }
        return nums[r];//r==l
    }
相关推荐
程序员-King.3 小时前
day158—回溯—全排列(LeetCode-46)
算法·leetcode·深度优先·回溯·递归
星火开发设计4 小时前
C++ 数组:一维数组的定义、遍历与常见操作
java·开发语言·数据结构·c++·学习·数组·知识
月挽清风4 小时前
代码随想录第七天:
数据结构·c++·算法
小O的算法实验室4 小时前
2026年AEI SCI1区TOP,基于改进 IRRT*-D* 算法的森林火灾救援场景下直升机轨迹规划,深度解析+性能实测
算法·论文复现·智能算法·智能算法改进
小郭团队5 小时前
2_1_七段式SVPWM (经典算法)算法理论与 MATLAB 实现详解
嵌入式硬件·算法·硬件架构·arm·dsp开发
充值修改昵称5 小时前
数据结构基础:从二叉树到多叉树数据结构进阶
数据结构·python·算法
Deepoch5 小时前
Deepoc数学大模型:发动机行业的算法引擎
人工智能·算法·机器人·发动机·deepoc·发动机行业
-To be number.wan5 小时前
【数据结构真题解析】哈希表中等难度挑战:冲突处理与查找效率深度剖析
数据结构·哈希算法
csdn_aspnet5 小时前
C 语言的优雅回归:从零手造数据结构
c语言·数据结构