【C++】競程筆記(雙指標)
【C++】競程筆記(雙指標)
程式碼範例參考:NTUCPC Guide,此筆記僅為個人學習用途。
雙指標(Two-Pointer)
雙指標是一種常用於陣列或字串處理的技巧。
顧名思義,雙指標是指同時使用兩個指標(指向資料結構的索引)來遍歷或操作資料。
這種技巧可以減少時間複雜度,能避免掉多重迴圈,常被用在排序陣列或雙向搜尋的情況上。
常見的指標移動方式有:
- 一個從頭開始、另一個從尾開始(雙向靠攏)
- 一個快指標、另一個慢指標(追趕法)
- 同時向同一方向移動但控制條件不同
有一個更快理解什麼是雙指標的例子:
假設有個已經排序好的序列:arr = [1, 3, 4, 5, 7, 10, 11]。
我們希望能找出兩個數的和是 14。
則雙指標的初始狀態:left → 1 3 4 5 7 10 11 ← right
首先走第一遍初始狀態:arr[left] + arr[right] = 12,值太小,需要移動左邊的指標,叫他往右,讓值變大一點。
所以第二遍:arr[left+1] + arr[right] = 14,剛好就找到 14 了。
像這種就是 Two sum 問題。
例題 1
Leetcode 209. Minimum Size Subarray Sum:https://leetcode.com/problems/minimum-size-subarray-sum/description/
最開始會想到的一定都會是暴力解,如這題可用三重迴圈去解決,只是時間複雜度會變成 $O(N^3)$ 。具體解法:
- 窮舉出所有 $(l, r)$
- 求出所有 $num[l]$ 到 $num[r]$ 的數字總和
- 看總和有沒有超過 $target$
雙指標做法:
- 使用兩個指標:l 為左界,r 為右界,用來定義一個動態的區間
[l, r]。 - r 從頭到尾遍歷陣列,每次將
nums[r]加入目前的總和 sum。 - 每當
sum >= target時,表示目前這個區間[l, r]是一組解(其子陣列總和符合要求)。 - 為了找到最短的子陣列和,就要把 l 往右推移,每推一次就更新當前最短長度,直到
sum < target為止。 - 重複步驟 2~4,直到整個陣列走完為止。
- 若最後沒有找到任何子陣列和(即最短長度仍為初始最大值),則回傳 0。
範例程式碼:
1 | int minSubArrayLen(int target, vector<int>& nums) { |
時間複雜度: $O(n)$ 。
例題 2
Leetcode 167. Two Sum II - Input Array Is Sorted:https://leetcode.com/problems/two-sum-ii-input-array-is-sorted/description/
解題思路:
- 初始化兩個指標:
left = 0、right = numbers.size() - 1。 - 計算
numbers[left] + numbers[right]:- 若和
= target:回傳left + 1和right + 1(題目要求 1-indexed)。 - 若和
< target:表示數字太小,將left往右移動。 - 若和
> target:表示數字太大,將right往左移動。
- 若和
- 因為題目保證有唯一解,故不需考慮無解情況。
程式碼:
1 | class Solution { |
習題
- Leetcode 3. Longest Substring Without Repeating Characters:https://leetcode.com/problems/longest-substring-without-repeating-characters/description/
解題思路:
- 初始化雙指標
l, r。 - 用
unordered_set記錄目前的字元。 - 當
r指到重複的字元,就不斷地從l開始移除,直到移除掉所有重複的字元。 - 每次更新最大長度。
1 | class Solution { |
- Leetcode 977. Squares of a Sorted Array:https://leetcode.com/problems/squares-of-a-sorted-array/description/
解題思路:
原陣列 nums 為非遞減排序,負數平方後可能比正數平方大:
1 | nums = [-4, -1, 0, 3, 10] |
若直接平方再排序的時間複雜度為 $O(nlogn)$ ,可用雙指標從兩端往中間夾,因為最大的平方數只可能出現在兩端。
- 建立左右指標
int l = 0, r = num.size() - 1;。 - 建立 result vector,從尾端填入最大的平方值(因為最大平方值在兩端)。
- 每次比較
abs(nums[left])和abs(nums[right]),取較大者平方後放到result[i]。
1 | class Solution { |




