👉 Two Pointers
雙指針在有序或對稱結構上往中間或同向移動。
18 題
📖 分類導讀
利用兩個指針在陣列或字串上移動,透過不同起點、方向或速度來高效解決問題。常見模式包括:相向指針(由兩端往中間)、同向指針(快慢指針)。
Notes:
- 通常需要先排序,或在已排序的資料上操作
- 相向指針常用於尋找配對,快慢指針常用於偵測環或找中點
三種移動策略
| 策略 | 指針怎麼動 | 典型用途 |
|---|---|---|
| 相向 (inward) | 一個從頭、一個從尾,往中間靠 | 已排序陣列找配對(兩數之和)、回文檢查、盛水容器 |
| 同向 (unidirectional) | 兩指針同方向,一個探路、一個記錄寫入位置 | 原地移除/去重、把符合條件的元素往前搬 |
| 分階段 (staged) | 先用一個指針定錨,再從它出發移動另一個 | 三數之和(固定一個再雙指針)、區段切分 |
之所以能把 O(n^2) 的巢狀枚舉降到 O(n),是因為輸入具有「可預測的動態」——已排序的單調性、回文的對稱性,讓我們每一步都能確定該移動哪一根指針,而不必回頭重掃。
TIP
訊號詞:「已排序」「找一對 / 三個和為某值」「回文」「原地 (in-place)」「不用額外空間」。看到這些就先想雙指針。
原地反轉技巧
陣列旋轉、反轉子段常用「三次反轉法」:要把 ab 變成 ba,等同 (a^R b^R)^R(各自反轉再整體反轉),全程 O(1) 額外空間。
fun reverse(nums: IntArray, from: Int, to: Int) {
var l = from
var r = to
while (l < r) {
val t = nums[l]; nums[l] = nums[r]; nums[r] = t
l++; r--
}
}
// 將陣列向左旋轉 k 步:reverse 前 k 段、reverse 後段、再整體 reverse
fun rotateLeft(nums: IntArray, k: Int) {
val n = nums.size
val s = k % n
reverse(nums, 0, s - 1)
reverse(nums, s, n - 1)
reverse(nums, 0, n - 1)
}
本章深入
- 快慢指針偵測環、找中點等套路,整理在 Linked List → 快慢指針 (Fast & Slow Pointers)。
跨倉庫導讀
- 對應理論章節:線性資料結構