LeetCode 高频题:双指针不是模板,是单调关系
LeetCode 高频题双指针不是模板是单调关系一、别把双指针背成口诀双指针是高频技巧但很多人只会背“左右指针往中间走”“快慢指针找环”。背模板能做几道题遇到变形就懵。双指针真正成立的前提是问题里存在某种单调关系移动某个指针后答案空间能被安全排除一部分。比如有序数组两数之和左小右大和太小就左移和太大就右移。这不是玄学是单调性保证了被排除的组合不可能成为答案。理解这个模板才不会乱用。二、推导链路先找单调性flowchart TD A[题目约束] -- B[是否有序或可排序] B -- C[移动指针会排除哪些答案] C -- D[证明排除安全] D -- E[写代码]不要一看到数组就双指针。先问指针移动后为什么不会漏答案如果回答不上来说明还没理解。三、代码示例有序数组两数之和def two_sum_sorted(nums, target): l, r 0, len(nums) - 1 while l r: s nums[l] nums[r] if s target: return [l, r] if s target: l 1 else: r - 1 return [-1, -1]复杂度是 O(n)空间 O(1)。关键不在代码短而在证明当 s target 时固定 l 搭配更小的右端只会更小所以 l 可以安全右移。这个证明比代码更重要。四、工程边界排序会改变原始索引很多双指针题需要排序但排序会改变原数组索引。如果题目要求返回原始下标就要保存值和索引。刷题时很容易在这里翻车。工程里也一样优化前要确认输出语义有没有被破坏。取舍方面排序后双指针通常是 O(n log n)哈希表可能是 O(n)但双指针空间更省也更容易处理去重和区间问题。不要迷信某一种方法要看题目要求。双指针还常用于滑动窗口。滑动窗口和左右夹逼不一样它维护的是一个区间并通过条件收缩左边界。它也依赖单调性右指针扩展后某些条件只会变强或变弱。理解这一点才能写对窗口收缩。再举一个反例如果数组里有负数很多“和不超过 k”的滑动窗口就不成立因为右指针扩展后窗口和可能变小左指针收缩后也可能变大单调性被破坏。这个时候还硬套模板就会漏答案。算法学习最怕只记成功案例不记失败条件。写题解时建议加一段“为什么不能用别的方法”。比如这题能不能哈希能不能二分为什么双指针更合适。比较方法能帮助读者建立选择能力而不是只学一份代码。最后双指针代码要特别注意循环条件。l r、l r、移动后是否跳过重复值都会影响边界。很多题不是思路错而是边界错。边界检查应该进入题解而不是留给读者自己撞墙。去重问题也很典型。三数之和里排序后固定一个数再用双指针找另外两个数。找到答案后需要跳过相同的左值和右值否则结果重复固定点也要跳过重复。这里的去重不是代码装饰而是输出语义要求。还有一类题需要“同向双指针”比如删除有序数组重复项。慢指针维护已处理区域快指针扫描新元素。它和左右夹逼完全不同。题解里把类型分清读者才不会把所有双指针混成一锅粥。分类清楚模板才有用。否则模板会反噬你自己本身。五、总结双指针不是模板而是建立在单调关系上的搜索空间剪枝。写代码前先证明移动指针不会漏答案才能在高频题里稳住。