二分查找老出错?5分钟搞懂二分查找常见陷阱

在深入探讨二分查找的边界处理之前,我们先来看看一些常见的错误场景。这些场景往往是因为对二分查找的原理理解不够深入,或者在编写代码时没有考虑到一些特殊情况而导致的。

一、90%的错误来自这两个场景

1. 边界值处理不当(重复元素陷阱)

错误本质:找到目标立即返回,忽略重复元素的连续分布特性

js 复制代码
// 错误:直接返回中间索引
function searchWrong(arr, target) {
    let left=0, right=arr.length-1
    while(left <= right) {
        const mid = (left+right)>>1
        if(arr[mid] === target) return mid // 可能返回中间位置
        else if(arr[mid] < target) left=mid+1
        else right=mid-1
    }
    return -1
}

// 正确:持续压缩右边界[2]
function searchLeftBound(arr, target) {
    let left=0, right=arr.length-1
    while(left <= right) {
        const mid = left + ((right-left)>>1)
        if(arr[mid] >= target) right=mid-1 // 关键!
        else left=mid+1
    }
    return arr[left]===target ? left : -1 // 最后校验
}

2. 区间缩小异常导致死循环

错误本质:区间收缩不彻底,形成无限震荡

js 复制代码
// 错误:right=mid导致区间无法缩小
while(left < right) {
    const mid = (left+right)>>1
    if(arr[mid] > target) right=mid // 应改为right=mid-1
    else left=mid+1
}

// 正确:严格保证区间收缩[7]
while(left <= right) {
    const mid = left + ((right-left)>>1)
    if(arr[mid] > target) right=mid-1 // 区间必须缩小
    else left=mid+1
}

二、六大边界处理要点(必读避坑指南)

1. 初始值陷阱

  • 闭区间 必须初始化right = arr.length-1,否则会漏查最后一个元素

  • 开区间 必须初始化right = arr.length,否则无法处理插入数组末尾的场景

2. 循环条件选择

区间类型 循环条件 搜索终止条件 典型错误案例
左闭右闭 left <= right left > right 漏查最后一个元素
左闭右开 left < right left == right 导致死循环

3. 中间值计算防溢出

javascript 复制代码
// 错误写法:可能导致整数溢出
mid = (left + right) / 2

// 正确写法:通过偏移量计算
mid = left + ((right - left) >> 1) // 位运算优化[1,6]

4. 边界校验不可少

左边界查找后必须验证

javascript 复制代码
if (left >= arr.length || arr[left] !== target) return -1 // [1,4]

右边界查找后必须回退

javascript 复制代码
return right >=0 && arr[right] === target ? right : -1 // [2,4]

5. 更新策略一致性

区间类型 条件判断 左指针更新 右指针更新
闭区间 arr[mid] < target left = mid + 1 right = mid - 1
开区间 arr[mid] >= target right = mid 不更新左指针

6. 极值处理方案

特殊场景 处理方案 LeetCode对应题目
空数组 预处理返回-1 #34
全相同元素 边界校验后二次验证 #34
目标值在两端 初始化时扩展虚拟边界 #35

二、两种核心写法精讲(附LeetCode实战案例)

写法1:左闭右闭区间(精确查找)

适用场景:判断元素是否存在、精确匹配查找

javascript 复制代码
function binarySearch(arr, target) {
    let left=0, right=arr.length-1 // [7]
    while(left <= right) { // 允许left=right的合法区间
        const mid = left + ((right-left)>>1) // 防溢出写法[1]
        if(arr[mid] === target) return mid   // 直接命中
        arr[mid] < target ? left=mid+1 : right=mid-1 // 区间严格收缩
    }
    return -1 // 未找到
}

LeetCode实战

  • 704.二分查找 通过率:72%
  • 时间复杂度:O(log n),空间复杂度:O(1)

写法2:左闭右开区间(边界查找)

适用场景:查找插入位置、左右边界定位

javascript 复制代码
function findBound(arr, target, isLeft) {
    let left=0, right=arr.length
    while(left < right) { // 终止时left=right
        const mid = left + ((right-left)>>1)
        if(arr[mid] < target || (!isLeft && arr[mid] === target)) {
            left = mid + 1 // 左区间收缩
        } else {
            right = mid    // 右区间收缩[4]
        }
    }
    return isLeft ? left : left-1 // 左右边界转换
}

LeetCode实战


三、红蓝染色法:可视化边界处理(应对面试追问)

操作原理

  1. 染色规则

    • 红色区域:确定不符合条件(arr[i] < target
    • 蓝色区域:可能符合条件(arr[i] >= target
  2. 指针移动策略

javascript 复制代码
function redBlueSearch(arr, target) {
    let left=-1, right=arr.length // 初始化虚拟边界
    while(left+1 < right) {       // 保证有中间元素
        const mid = left + ((right-left)>>1)
        arr[mid] < target ? left=mid : right=mid // 染色规则
    }
    return arr[right]===target ? right : -1 // 最终校验
}

技术优势

  • 无需处理mid±1的边界问题
  • 循环终止条件更直观(left+1 == right
  • 可扩展性强,适合处理复杂边界问题

四、高频面试考点

  1. 如何防止死循环?

    • 核心:确保每次循环区间严格缩小(leftright必须移动)

    • 案例:当left=3, right=4mid=3时,必须让left=mid+1right=mid

  2. 如何处理重复元素?

    • 左边界:找到目标后继续左移right指针

    • 右边界:找到目标后继续右移left指针

  3. 为什么推荐位运算?

    • 位运算>>1比除法运算/2快2-3倍

    • 避免JS引擎的浮点数转换开销

相关推荐
yuanbenshidiaos12 分钟前
面试问题总结:qt工程师/c++工程师
c++·qt·面试
半盏茶香17 分钟前
启幕数据结构算法雅航新章,穿梭C++梦幻领域的探索之旅——堆的应用之堆排、Top-K问题
java·开发语言·数据结构·c++·python·算法·链表
uhakadotcom20 分钟前
Langflow:打造AI应用的强大工具
前端·面试·github
uhakadotcom27 分钟前
🤖 LangGraph 多智能体群集
面试·架构·github
小竹子1440 分钟前
L1-1 天梯赛座位分配
数据结构·c++·算法
董董灿是个攻城狮1 小时前
Transformer 通关秘籍8:词向量如何表示近义词?
算法
uhakadotcom1 小时前
Caddy Web服务器初体验:简洁高效的现代选择
前端·面试·github
独好紫罗兰1 小时前
洛谷题单2-P5712 【深基3.例4】Apples-python-流程图重构
开发语言·python·算法
uhakadotcom1 小时前
NVIDIA Resiliency Extension(NVRx)简介:提高PyTorch训练的容错性
算法·面试·github
专业抄代码选手2 小时前
【JS】instanceof 和 typeof 的使用
前端·javascript·面试