对撞指针(相向)
适用:有序数组求两数之和、盛水、回文、三数之和(固定一端 + 双指针)。
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 []
排序后 sum < target 只能增大左端;sum > target 只能减小右端,每步排除一种组合,总 O(n)。
三数之和:外层枚举 i,内层对 [i+1, n-1] 用对撞指针,注意跳过重复元素。
快慢指针(同向)
适用:链表环、找中点、删除倒数第 k、原地去重、数组划分。
- 环检测:快指针每次走 2 步,慢走 1 步,相遇则有环(Floyd)。
- 划分:
slow维护有效区,fast扫描,如「移除元素」「移动零」。
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
滑动窗口(子串 / 子数组)
维护 [left, right] 区间,用哈希或数组记录窗口内字符/计数,在单调条件下移动。
最长无重复子串:
def length_of_longest_substring(s):
last = {}
left = ans = 0
for right, ch in enumerate(s):
if ch in last and last[ch] >= left:
left = last[ch] + 1
last[ch] = right
ans = max(ans, right - left + 1)
return ans
可变窗口求最小长度(和 >= target):右扩累加和,满足时不断左缩并更新最小长度。
def min_subarray_len(target, nums):
left = total = 0
ans = float("inf")
for right, x in enumerate(nums):
total += x
while total >= target:
ans = min(ans, right - left + 1)
total -= nums[left]
left += 1
return 0 if ans == float("inf") else ans
面试归纳
| 信号 | 套路 | |---|---| | 有序 + 配对 | 对撞 | | 子串/连续子数组 + 约束 | 滑动窗口 | | 链表环/中点 | 快慢 | | 原地 O(1) 空间 | 同向双指针 |
写代码时明确:不变量(窗口内满足什么)、何时移动 left、答案在扩张还是收缩时更新。LeetCode 3、76、209、15、11 是高频起点。