算法踩坑-二分法
二分查找踩坑全记录:从基础模板到边界查找,我踩过的所有雷 | 刷题踩坑连载 01
本系列持续更新算法刷题过程中亲手踩过的真实坑点,不照搬标准答案,只记录翻车现场、错误根源和修正思路,既是自己的知识沉淀,也帮你避开同类低级失误。
本文是二分查找模块的完整踩坑汇总,覆盖从基础目标值查找,到左右边界变体的全场景错误。从核心逻辑到语法细节,从普通用例到极端边界,所有踩过的雷都整理在此,做完二分所有题目后对照自查,能规避绝大多数不必要的报错。
一、基础二分通用坑:所有题型都要先避开的雷
这部分是二分查找的底层共性错误,无论是基础题还是边界变体题都会踩,属于写完代码第一优先级自查的内容。
1. 核心逻辑类(全语言通用)
这类错误不区分编程语言,是对二分区间逻辑理解不到位导致的,直接决定代码能不能跑通。
-
无边界意识,搜索区间不收敛 不定义
left/right双指针,直接用数组长度和 mid 推导新 mid,区间永远不会缩小,最终必然死循环超时。本质是没理解二分的核心:用左右指针不断收缩搜索范围,每次砍掉一半区间。 -
边界移动方向完全颠倒
- 当
nums[mid] < target(目标值在 mid 右侧):错误移动右边界right,正确应收缩左边界left = mid + 1 - 当
nums[mid] > target(目标值在 mid 左侧):错误移动左边界left,正确应收缩右边界right = mid - 1
-
边界不越过 mid,区间收不干净 写成
left = mid/right = mid,区间永远残留 mid 这个元素,当剩下两个元素时会陷入死循环。左闭右闭模板下,mid 已经被比较过了,收缩边界时必须跳过它,用mid ± 1。 -
循环条件用错,空转或越界 用
nums[mid] != target作为循环条件,一旦目标不存在,循环会无限空转甚至索引越界。正确循环条件是left <= right,对应左闭右闭区间的终止规则:当右指针跑到左指针左边,说明区间已空,搜索结束。 -
漏掉命中分支,找到也不返回 循环内只处理大于、小于两种情况,不写
== target的返回逻辑,就算找到目标也不会退出,全程空转超时。 -
重复计算 mid,平白引入错误 循环开头已经计算过 mid,分支里又手动重复计算,还乱加偏移量,属于多余操作带来的无意义错误。
-
区间定义前后不统一 收缩边界用
mid ± 1(对应左闭右闭区间),但初始右边界却写成nums.length(对应左闭右开区间),从根上逻辑矛盾,边界必然出错。
2. Java 语法专属类
这类是 Java 语言特性导致的低级错误,编译阶段就会暴露,但初学者很容易反复踩。
-
数组长度写法颠倒 误写为
length.nums,正确写法是nums.length。 -
分支内重复声明变量 在 if 块里写
int left = .../int right = ...,相当于新建了同名局部变量,外层的左右指针根本没被修改,循环永远不会结束。修正:直接赋值left = mid + 1,不要加变量类型声明。 -
误用三元返回语法 Java 没有
return A if B else C的写法;且基础二分场景完全不需要,循环结束直接return -1即可。 -
变量作用域混淆
mid在 while 循环内部定义,循环结束后作用域失效,循环外访问nums[mid]会直接编译报错。
二、进阶踩坑:左右边界查找专属(LeetCode 34 实战复盘)
掌握基础二分后,做「查找元素首尾位置」这类边界题时,很容易以为只是改改分支逻辑,实则在偏移量、健壮性上踩新的坑。以下是 LeetCode 34「在排序数组中查找元素的第一个和最后一个位置」的专属翻车点。
1. 语法细节翻车:复制代码带来的低级失误
-
变量名笔误 复制第一段二分逻辑改右边界代码时,变量名没改干净,出现未定义的
target_这类变量,直接编译失败。建议:复用逻辑尽量封装成方法,少用整段复制的方式写代码,减少手滑概率。 -
数组初始化与比较语法混淆 用
[a, b]格式直接创建数组、用==比较两个数组的内容,都是混淆了其他语言的语法。 Java 中:
- 新建数组必须用
new int[]{元素1, 元素2}的格式 ==对比的是数组内存地址,不是元素内容,永远不会返回 true 修正:直接通过边界值构造返回数组,不需要做数组整体比较。
2. 逻辑偏移错误:多移一位答案全错
-
左边界结果多加 1 循环结束后用
left + 1作为左边界,属于对指针最终位置理解偏差。 左闭右闭模板下,命中目标时收缩右边界right = mid - 1,循环终止时right = left - 1,此时left天然指向第一个等于 target 的元素,不需要额外偏移。 结论:左边界查找,结束后直接取left。 -
右边界结果多减 1 循环结束后用
right_ - 1作为右边界,同理属于多余偏移。 命中目标时收缩左边界left_ = mid_ + 1,循环终止时right_天然指向最后一个等于 target 的元素,不需要额外减 1。 结论:右边界查找,结束后直接取right_。
3. 边界健壮性缺失:极端用例直接崩溃
普通用例能跑通,但空数组、目标值超出数组范围的场景直接触发越界异常,属于最容易忽略的点。
-
只防左越界,漏掉右越界 只判断
left < 0,没判断left >= nums.length。 触发场景:输入为空数组、target 比所有元素都大,此时left会等于数组长度,直接访问nums[left]就会抛出数组下标越界异常。 修正:越界必须双向判断:left < 0 || left >= nums.length。 -
越界条件写反,等于没设防 把
>=写成<,写成left < nums.length,相当于把「合法条件」当成了「越界条件」,完全起不到防护作用。 记住:数组合法下标范围是0 <= index < 数组长度,越界就是取反的结果。 -
忽略短路规则,先访问再判断 Java 的
||是短路或,从左到右依次判断,前面条件成立则后面代码不执行。 必须把「索引合法性判断」放在最左侧,先拦下非法索引,绝对不会执行到nums[left],自然不会越界。如果把数值比较放在前面,空数组场景会先访问数组再判断,直接触发异常。
三、二分查找通用自检三步法
无论是基础题还是边界变体题,写完代码按这三步自查一遍,能规避 90% 以上的低级错误:
-
语法自检 检查变量名是否笔误、数组返回格式是否正确、变量作用域是否合法,先保证代码能编译通过。
-
逻辑偏移自检
- 基础题:确认边界移动方向正确、循环条件匹配区间定义、命中即返回
- 边界题:左边界看
left,右边界看right,不要额外加减偏移量
- 健壮性自检 先判断索引是否在合法范围内,再访问数组元素;覆盖空数组、目标值超出数组范围的极端场景。
二分查找模块的踩坑暂告一段落,后续学习新算法模块时,会持续更新本系列的踩坑记录。