(Day9)演算法復健運動for藍橋杯-雙指標
先粘一個部落格:素材來源https://www.cnblogs.com/luoyj/p/12408871.html
尺取法(又稱為:雙指標、two pointers)是演算法競賽中一個常用的最佳化技巧,用來解決序列的區間問題,操作簡單、容易程式設計。
如果區間是單調的,也常常用二分法來求解,所以很多問題用尺取法和二分法都行。
另外,尺取法的的操作過程和分治演算法的步驟很相似,有時候也用在分治中。
要點:
(1)一般序列都是有序的,無序的可以先排序。
(2)問題和序列的區間有關,且需要操作2個變數,可以用兩個下標(指標)i、j掃描區間。
兩個方向:
(a)反向掃描。i、j方向相反,i從頭到尾,j從尾到頭,在中間相會。
(b)同向掃描。i、j方向相同,都從頭到尾,可以讓j跑在i前面。
在leetcode的一篇文章中 常用的雙指標技巧 https://leetcode-cn.com/circle/article/GMopsy/,把同向掃描的i、j指標稱為“快慢指標”,把反向掃描的i、j指標稱為“左右指標”,更加形象。快慢指標在序列上產生了一個大小可變的“滑動視窗”,有靈活的應用,例如3.1的“尋找區間和”問題。
反向掃描
例題1:找一個序列其中兩個數加起來為m。(序列是有序的)
模板程式碼:
void find_sum(int a[], int n, int m)
{
sort(a, a + n - 1); //先排序,複雜度O(nlogn)
int i = 0, j = n - 1; //i指向頭,j指向尾
while (i < j)//複雜度O(n)
{
int sum = a[i] + a[j];
if (sum > m) j--;
if (sum < m) i++;
if (sum == m)
{
cout << a[i] << " " << a[j] << endl; //列印一種情況
i++; //可能有多個答案,繼續
}
}
}
在這個題目中,尺取法不僅效率高,而且不需要額外的空間。
把題目的條件改變一下,可以變化為類似的問題,例如:判斷一個數是否為兩個數的平方和
判斷迴文串:
這個自己想,一個從第一個開始跑,一個從最後一個開始跑
同向掃描
這是用尺取法產生“滑動視窗”的典型例子。
∎問題描述
給定一個長度為n的陣列a[]和一個數s,在這個陣列中找一個區間,使得這個區間之和等於s。輸出區間的起點和終點位置。
樣例輸入:
15
6 1 2 3 4 6 4 2 8 9 10 11 12 13 14
6
樣例輸出:
0 0
1 3
5 5
6 7
說明:樣例輸入的第1行是n=15,第2行是陣列a[],第3行是區間和s=6。樣例輸出,共有4個情況。
∎題解
指標i和j,i<=j,都從頭向尾掃描,判斷區間[i,j]的和是否等於s。
如何尋找區間和等於s的區間?如果簡單地對i和j做二重迴圈,複雜度是O(n2)。用尺取法,複雜度O(n),操作步驟是:
(1)初始值i=0、j=0,即開始都指向第一個元素a[0]。定義sum是區間[i, j]的和,初始值sum = a[0]。
(2)如果sum等於s,輸出一個解。繼續,把sum減掉元素a[i],並把i往後移動一位。
(3)如果sum大於s,讓sum減掉元素a[i],並把i往後移動一位。
(4)如果sum小於s,把j往後挪一位,並把sum的值加上這個新元素。
在上面的步驟中,有2個關鍵技巧:
(1)滑動視窗的實現。視窗就是區間[i,j],隨著i和j從頭到尾移動,視窗就“滑動”掃描了整個序列,檢索了所有的資料。i和j並不是同步增加的,視窗像一隻蚯蚓伸縮前進,它的長度是變化的,這個變化,正對應了對區間和的計算。
(2)sum的使用。如何計算區間和?暴力的方法是從a[i]到a[j]累加,但是,這個累加的複雜度是O(n)的,會超時。如果利用sum,每次移動i或j的時候,只需要把sum加或減一次,就得到了區間和,複雜度是O(1)。這是“字首和”遞推思想的應用。
下面是程式碼。
int i = 0, j = 0;
int sum = a[0];
while(j < n)
{ //下面程式碼中保證 i<=j
if(sum >= s)
{
if(sum == s)
printf("%d %d\n", i, j);
sum -= a[i];
i++;
if(i>j)
{
sum = a[i];
j++;
} //防止i超過j
}
if(sum < s)
{
j++;
sum += a[j];
}
}
“滑動視窗”的例子還有:
(1)給定一個序列,以及一個整數M;在序列中找M個連續遞增的元素,使它們的區間和最大。
(2)給定一個序列,以及一個整數K;求一個最短的連續子序列,其中包含至少K個不同的元素。
在“4 典型題目”中有相似的題目。
陣列去重
很簡單,自己想