
雙指標的核心就一句話:用兩根指標把 O(n²) 降成 O(n)。聽起來很簡單,但知道什麼時候能用、怎麼移動指標,才是真功夫。
先講結論
雙指標有三種:對撞指標(兩端往中間)、快慢指標(同向不同速)、滑動視窗(維護動態區間)。面試考雙指標的頻率極高,尤其是陣列和鏈表題,大概有三成可以用雙指標解。
對撞指標:從兩端夾擊
兩數之和(已排序),暴力 O(n²),對撞指標 O(n):
[2, 7, 11, 15] target=9
↑ ↑
left right
2 + 15 = 17 > 9 → right--
2 + 11 = 13 > 9 → right--
2 + 7 = 9 ✓
為什麼可以移動?因為陣列已排序。sum > target 時,right 往左才能變小;sum < target 時,left 往右才能變大。每次都在有效地排除不可能的組合。
int left = 0, right = nums.length - 1;
while (left < right) {
int sum = nums[left] + nums[right];
if (sum == target) return new int[]{left, right};
if (sum < target) left++;
else right--;
}三數之和:固定一個,剩下兩個對撞
for (int i = 0; i < n - 2; i++) {
if (i > 0 && nums[i] == nums[i-1]) continue; // 跳過重複
int left = i + 1, right = n - 1;
while (left < right) {
// 對撞找 target = -nums[i]
}
}從 O(n³) 降到 O(n²)。面試經典中的經典。
回文判斷
while (left < right) {
if (s.charAt(left) != s.charAt(right)) return false;
left++;
right--;
}對撞指標的最簡單應用。
快慢指標:同向不同速
陣列版:移除元素
slow 指向「下一個要放的位置」,fast 負責掃描:
int slow = 0;
for (int fast = 0; fast < n; fast++) {
if (nums[fast] != val) {
nums[slow++] = nums[fast];
}
}
return slow; // 新長度移除重複、移動零、都是同一個模式。fast 找有效元素,slow 負責收集。
鏈表版:檢測環
快指標一次走兩步,慢指標一次走一步。有環的話,快的遲早追上慢的。
ListNode slow = head, fast = head;
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
if (slow == fast) return true;
}
return false;為什麼一定會相遇?因為快指標每步比慢指標多走一格,所以每一步「距離差」都縮小 1。想像你在操場跑步,跑得快的人終究會追上跑得慢的人。
找環入口:相遇後,一根指標回到 head,兩根都改成一步一步走,再次相遇就是入口。數學證明要用到 Floyd’s Cycle Detection,這裡就不展開了——面試記結論就行。
找鏈表中點
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
}
return slow;找倒數第 k 個
fast 先走 k 步,然後 fast 和 slow 一起走。fast 到底的時候 slow 就在倒數第 k 個。
for (int i = 0; i < k; i++) fast = fast.next;
while (fast != null) {
slow = slow.next;
fast = fast.next;
}
return slow;什麼時候用雙指標?
問自己:
- 陣列/字串已排序嗎?→ 對撞指標
- 需要 in-place 操作嗎?→ 快慢指標
- 鏈表有環/找中點/找倒數?→ 快慢指標
- 連續子陣列/子字串?→ 滑動視窗
雙指標的好處是空間 O(1)。很多題目用 Hash Map 也能解,但面試官追問「能不能不用額外空間」的時候,雙指標就是你的答案。
雙指標技巧就像筷子:兩根棍子單獨沒用,但配合好了什麼都能夾。關鍵是知道什麼時候該動哪一根。