1. 为什么LeetCode高频题总在等号上栽跟头?
上周帮学弟review代码时,又看到熟悉的场景:他盯着屏幕喃喃自语"逻辑明明都对啊",结果一查发现是while(left < right)该写成while(left <= right)。这已经是本月第三次遇到同样的问题了。作为刷过3遍Hot 100的老手,我决定系统梳理这些"一提交就错"的等号陷阱。
算法题中约40%的提交错误源于边界条件处理不当,而其中又有超过60%集中在等号使用上。不同于算法思路的错误,这类问题往往具有极强的隐蔽性——本地测试用例能过,但提交就是WA。更恼人的是,一旦你真正理解其中规律,这些错误完全可以避免。
2. 二分查找:等号使用重灾区
2.1 循环条件中的生死抉择
新手最容易栽跟头的地方就是二分查找的循环条件。来看这段经典代码:
python复制# 错误示范
while left < right:
mid = (left + right) // 2
if nums[mid] == target:
return mid
elif nums[mid] < target:
left = mid
else:
right = mid
这段代码有两个致命问题:
- 循环条件应该用
<=而非< - 区间收缩时没有
±1
闭区间原则:当使用left <= right时,我们维护的是闭区间[left, right]。这意味着:
- 最后一次循环时left=right=mid
- 所有元素都会被检查
- 对于单元素数组也能正确处理
而left < right会导致:
- 当left=right时循环直接退出
- 最后一个元素永远不被检查
- 对单元素数组直接返回-1
2.2 区间收缩的魔鬼细节
正确的二分模板应该这样写:
python复制left, right = 0, len(nums) - 1
while left <= right:
mid = (left + right) // 2
if nums[mid] == target:
return mid
elif nums[mid] < target:
left = mid + 1 # 关键点1
else:
right = mid - 1 # 关键点2
±1原则:在确定mid不是解后,必须将区间完全越过mid。如果不加1:
- 当left和right相邻时会导致死循环
- 例如left=3, right=4且nums[3]<target时
- 不加1则left永远停留在3
2.3 边界查找的等号玄学
在寻找左边界/右边界的变种中,等号决定搜索方向:
python复制# 找第一个≥target的位置
if nums[mid] >= target:
right = mid
else:
left = mid + 1
# 找最后一个≤target的位置
if nums[mid] <= target:
left = mid
else:
right = mid - 1
方向性原则:
>=使搜索向左收敛,保留可能解<=使搜索向右收敛,保留可能解- 写反会导致跳过正确边界
实战技巧:在纸上画出
[1,2,2,2,3]找2的边界情况,体会等号如何影响指针移动
3. 滑动窗口:等号决定窗口大小
3.1 最小子数组问题的经典陷阱
以LeetCode 209题为例,求满足sum ≥ target的最短子数组长度。常见错误写法:
python复制while sum > target: # 应该用>=
sum -= nums[left]
left += 1
包含边界原则:题目要求的是"≥"而非">"。当sum刚好等于target时:
- 用
>会错过更新最小长度的机会 - 导致最终结果比实际解大1
- 对于target=7, nums=[2,3,1,2,4,3]会得到4而非正确答案2
3.2 无重复字符子串的特殊处理
在LeetCode 3题中,等号出现在不同位置:
python复制while right < len(s):
if s[right] in window:
# 这里必须用≥
while window[s[left]] != s[right]:
del window[s[left]]
left += 1
left += 1
重复判定原则:当发现重复字符时:
- 需要收缩左边界直到排除重复
- 使用
≥确保完全清除重复字符的所有影响 - 漏掉等号可能导致窗口中仍保留部分重复字符
4. 双指针:等号影响指针命运
4.1 两数之和的边界艺术
在LeetCode 167题中,等号使用有特殊规则:
python复制while left < right: # 这里不能用≤
total = nums[left] + nums[right]
if total == target:
return [left+1, right+1]
elif total < target:
left += 1
else:
right -= 1
唯一性原则:使用<而非<=是因为:
- 每个元素只能使用一次
- 当left=right时意味着使用同一个元素两次
- 这与题目要求相违背
4.2 盛水容器中的等号哲学
LeetCode 11题的指针移动策略:
python复制if height[left] <= height[right]: # 这里必须用≤
left += 1
else:
right -= 1
移动策略原则:
- 当两边高度相等时,必须选择移动其中一个指针
- 如果去掉等号,遇到height[left]=height[right]时会陷入死循环
- 移动较短边才能保证不漏解
4.3 三数之和去重的关键细节
在LeetCode 15题中,去重逻辑依赖等号:
python复制while i < j and nums[i] == nums[i+1]: # 必须带==
i += 1
去重完备原则:
- 使用
==确保跳过所有重复元素 - 漏掉等号会导致部分重复解被保留
- 对于
[-2,-2,0,2,2]会输出重复的三元组
5. 单调栈:等号定义单调性
5.1 下一个更大元素的严格性
LeetCode 496题中,单调栈的维护:
python复制while stack and nums[i] > stack[-1]: # 严格>
res[stack.pop()] = nums[i]
stack.append(i)
对比LeetCode 901题股票跨度:
python复制while stack and price >= stack[-1][0]: # 非严格≥
count += stack.pop()[1]
单调类型原则:
>维护严格单调递减栈≥维护非严格单调递减栈- 混淆两者会导致计算结果完全错误
6. 二叉树:等号守护递归边界
6.1 递归终止条件的铁律
计算二叉树深度时的基础写法:
python复制def maxDepth(root):
if root == None: # 必须用==
return 0
return 1 + max(maxDepth(root.left), maxDepth(root.right))
边界安全原则:
== None明确表示递归到底- 漏掉等号会导致无限递归
- 对于空树应该返回0而非报错
6.2 BFS队列判空的正确姿势
层序遍历的标准写法:
python复制while queue: # 等价于while not queue.empty()
size = len(queue)
for _ in range(size):
node = queue.popleft()
# 处理节点
容器判空原则:
- 使用
while queue最简洁安全 - 某些语言中
queue.empty()可能不可靠 - 判空失败会导致空指针异常
7. 排序与贪心:等号保证稳定性
7.1 合并区间的排序关键
LeetCode 56题的核心排序:
python复制intervals.sort(key=lambda x: x[0]) # 必须带等号比较
排序完备原则:
- 相等的情况也需要正常排序
- 否则无法保证相邻区间能正确合并
- 对于
[[1,4],[2,3]]会得到错误结果
7.2 最大数比较的等号智慧
LeetCode 179题的自定义排序:
python复制def compare(a, b):
if a + b == b + a: # 必须处理相等
return 0
return -1 if a + b > b + a else 1
相等处理原则:
- 显式处理相等情况使排序稳定
- 忽略等号可能导致结果不一致
- 对于
[3,30,34]会得到不同排列
8. 等号检查四步法(实战口诀)
根据上述所有案例,我总结出这套检查流程:
-
区间确认:当前维护的是开区间还是闭区间?
- 闭区间必带
=(如二分查找) - 开区间不带
=(如两数之和)
- 闭区间必带
-
题意确认:题目要求的是
>还是≥?- 滑动窗口类通常带
= - 去重类必须带
==
- 滑动窗口类通常带
-
死循环检查:指针移动是否会卡住?
- 双指针在相等时必须移动一边
- 二分收缩必须
±1
-
边界测试:三个必测用例:
- 空输入(如None、[])
- 单元素输入
- 所有元素相等的特殊情况
把这四步做成checklist贴在显示器旁,能避免90%的等号相关错误。下次当你"逻辑都对但提交就是错"时,先按这个清单检查一遍。