二分查找细节理解

要理解二分查找中 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
    }
相关推荐
仰泳的熊猫2 小时前
题目2570:蓝桥杯2020年第十一届省赛真题-成绩分析
数据结构·c++·算法·蓝桥杯
无极低码5 小时前
ecGlypher新手安装分步指南(标准化流程)
人工智能·算法·自然语言处理·大模型·rag
软件算法开发6 小时前
基于海象优化算法的LSTM网络模型(WOA-LSTM)的一维时间序列预测matlab仿真
算法·matlab·lstm·一维时间序列预测·woa-lstm·海象优化
罗超驿6 小时前
独立实现双向链表_LinkedList
java·数据结构·链表·linkedlist
superior tigre6 小时前
22 括号生成
算法·深度优先
努力也学不会java7 小时前
【缓存算法】一篇文章带你彻底搞懂面试高频题LRU/LFU
java·数据结构·人工智能·算法·缓存·面试
旖-旎8 小时前
二分查找(x的平方根)(4)
c++·算法·二分查找·力扣·双指针
ECT-OS-JiuHuaShan8 小时前
朱梁万有递归元定理,重构《易经》
算法·重构
智者知已应修善业9 小时前
【51单片机独立按键控制数码管移动反向,2片74CH573/74CH273段和位,按键按下保持原状态】2023-3-25
经验分享·笔记·单片机·嵌入式硬件·算法·51单片机