为什么双指针能降复杂度
暴力枚举两个下标是 O(n²)。双指针利用 移动方向的单调性:每轮至少有一个指针只朝一个方向走,总移动次数 O(n),从而把平方级降为线性。
Week 1 的数组基础见 week01-array-basics;双指针常与排序、滑动窗口组合。站内 /chapters/algorithm-lab/problems 提供了可交互练习环境。
对撞指针(相向)
两个指针从两端向中间移动,经典于 有序数组两数之和:
def two_sum_sorted(nums, target):
i, j = 0, len(nums) - 1
while i < j:
s = nums[i] + nums[j]
if s == target:
return [i, j]
elif s < target:
i += 1
else:
j -= 1
return []
盛最多水的容器:同样对撞,比较面积后移动较短边(短板决定高度,移长边不可能更大)。
三数之和:外层枚举第一个数并去重,内层对撞求两数之和为 -nums[i],注意跳过重复的 (i, left) 组合。
快慢指针(同向)
链表环检测(Floyd):快指针走 2 步、慢指针 1 步,相遇则有环;入环点再设一指针从头同步走,相遇处为环入口。
def has_cycle(head):
slow = fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
if slow is fast:
return True
return False
数组原地划分:slow 维护「有效区」边界,fast 扫描。典型题:移除元素、移动零、荷兰国旗(三路划分)。
找重复数 / 环入点变体:将数组下标视为「next 指针」,等价链表环问题。
合并与去重
合并两个有序数组:双指针比较,小者写入结果;一方耗尽则拷贝剩余。
有序数组去重:slow 指向下一个写入位置,fast 扫描,仅当 nums[fast] != nums[slow-1] 时写入。
def remove_duplicates(nums):
if not nums:
return 0
slow = 1
for fast in range(1, len(nums)):
if nums[fast] != nums[fast - 1]:
nums[slow] = nums[fast]
slow += 1
return slow
面试表达模板
i < j、fast 空指针、全重复数组返回 1。与 week01-sliding-window 的区别:滑动窗口维护 连续区间 的统计量;双指针更宽,包含非连续的同向扫描。读题时先问「是否必须连续子数组/子串」。
练习建议
每天 2 题:1 道对撞 + 1 道快慢。错题记录「单调性来自哪里」,比记代码更有用。
实战巩固与面试表达
本篇属于 8 周冲刺 week01-two-pointers 主题。复习时先闭卷回答 frontmatter 中三张 flashcard,再展开口述两个「为什么」:为什么这种方案能 work、边界失败时如何降级。与相邻章节对照:算法篇强调复杂度与模板,Go 篇强调工程默认写法,中间件篇强调线上故障案例。
动手与自检清单
用 25 分钟限时做 1 道相关练习题或画出一张架构/数据结构示意图;用 5 分钟写 STAR 片段说明你在项目里是否用过类似技术。记录 3 个面试追问及你的标准答法,存入 /zh/notebook/master-plan 笔记。若某点不熟,回到对应 /chapters 交互 Lab 重新走一遍流程,比死记卡片更有效。
易错点提醒
避免只背名词不会画图;避免只说优点不谈 trade-off(性能、一致性、运维成本至少提一项);避免把学习 Demo 说成百万 QPS 生产。回答时使用「场景 → 方案 → 结果 → 反思」四段式,体现工程成熟度。