基本雙指針

罕見

介紹

雙指針(Two pointer)是一種常見的技巧,概念就是用兩個變數指向陣列中不同的位置,然後根據問題的要求,移動這兩個指針。通常可以利用單調性來以更好的時間複雜度解決問題。

合併兩個排序陣列

一個基本的題型是,給定兩個排序過的陣列,要求合併這兩個陣列並且保持排序。

這個問題就可以使用雙指針解決!

也許你已經想到了,我們可以用兩個指針分別指向兩個陣列的開頭,然後比較這兩個位置的元素:

alt text

把較小的元素放入答案陣列,然後移動指向這個元素的指針:

alt text

接下來繼續比較兩個指針指向的元素:

alt text

重複這個過程直到其中一個陣列的元素全部被放入答案陣列:

alt text

如此一來,我們就在 O(n)O(n) 的時間複雜度內完成了這個問題。

程式碼也非常簡單:

// 雙指針主要的部分
int i = 0, j = 0;
vector<int> ans;
while (i < nums1.size() && j < nums2.size()) {
    if (nums1[i] < nums2[j]) {
        ans.push_back(nums1[i]);
        i++;
    } else {
        ans.push_back(nums2[j]);
        j++;
    }
}

// 把剩下的元素放入答案陣列
while (i < nums1.size()) {
    ans.push_back(nums1[i]);
    i++;
}

while (j < nums2.size()) {
    ans.push_back(nums2[j]);
    j++;
}

當然,從上面的例子可以發現,這個方法只能用在兩個排序過的陣列上。如果陣列沒有排序,那麼這個方法就不適用了。也就是說,大部分的雙指針問題都會依賴於陣列的單調性。

進階題

我們有一個長度為 nn 的陣列 aa 和一個長度為 mm 的陣列 bb,在兩個陣列皆是排序的情況下,請對於每個 bib_i 計算有多少陣列 aa 中的元素嚴格小於 bib_i

1n,m1051 \leq n, m \leq 10^51ai,bi1091 \leq a_i, b_i \leq 10^9

解決問題的第一步就是觀察,我們可以在這個問題中看到什麼單調性呢?

如果 aa 陣列中存在 kk 個比 bib_i 小的元素,那麼對於 bi+1b_{i+1} 來說,答案一定不會比 kk 小。

看看下面的例子,陣列 bb 中的 131333 個比它小的元素:

alt text

那麼對於 bb 中的 1515 來說,答案一定不可能比 33 小:

alt text

根據這個性質,我們可以使用雙指針解決這個問題:

指針 ii 指向陣列 aa 目前比較到的位置,指針 jj 指向陣列 bb 目前比較到的位置,每次指針 jj 往右移動時,我們就把指針 ii 往右移動直到 a[i]b[j]a[i] \geq b[j],並把 ii 的值當作答案記錄下來。

for (int i = 0, j = 0; j < b.size(); j++) {
    while (i < a.size() && a[i] < b[j]) {
        i++;
    }
    ans[j] = i;
}