有時候我們要對一個數組進行i和j兩個下標的雙重循環枚舉:
如圖,紅色的i下標指向第一個元素時,綠色的j要從第二個元素到最后一個元素枚舉一遍;i下標指向第2個元素時,綠色的j要從第3個元素到最后一個元素枚舉一遍。以此類推。這是一個O(N2)的復雜度。

雙指針的思路是什么呢?就是在某些情況下,根據題目要求,j下標並不需要從i+1開始重新向后枚舉一遍,而是跟隨着i向后移動,j也向后移動。
如圖,當紅色i下標向右移動時,綠色j下標是不會向左移動的。它有可能不動,也有可能向右,但是不會向左。由於j坐標不會向左移動,所以整個枚舉的復雜度是O(N)的,比O(N2)低了一個數量級。

【例題分析】
給定N個整數A1, A2, … ,AN,以及一個正整數K。問在所有的大於等於K的兩個數的差(Ai-Aj)中,最小的差是多少。(N <= 100000)
雙指針優化:
首先就是對A數組排序。比如假設排好序的A數組是: A=[1, 3, 7, 8, 10, 15], K=3 這時我們枚舉兩個數中較小的是A[i],較大的數是A[j];對於A[i]來說,我們要找到最優的A[j],也就是最小的A[j]滿足A[j]-A[i]>=k
如下圖:
- 當A[i]=1的時候,最優的A[j]=7
- 當A[i]=3的時候,最優的A[j]=7
- 當A[i]=7的時候,最優的A[j]=10
- 當A[i]=8的時候,最優的A[j]=15
- 當A[i]=10的時候,最優的A[j]=15
當i依次向右的時候,這個“最優的A[j]”或者不動或者向右,不會向左。換句話說,我現在已知A[i]=1的時候A[j]=7是最優的解;那當A[i]變成3的時候,A[j]我就可以從7位置向后找,不用再向前找。

代碼如下:
1 #include <iostream> 2 #include <algorithm> 3 using namespace std; 4 5 int main() 6 { 7 int n, k, min; 8 int a[100000]; 9 10 cin >> n >> k; 11 for(int i = 0; i < n; i ++) 12 cin >> a[i]; 13 sort(a, a + n); //遞增排序 14 // 無解情況下 15 if(a[n-1] - a[0] < k ) 16 { 17 cout << "no solution" << endl; 18 return 0; 19 } 20 //有解情況下 21 min = a[n-1] - a[0]; 22 int j = 0; 23 for(int i = 0; i < n; i ++) 24 { 25 while( j <= n - 1 && a[j] - a[i] < k ) 26 j ++; 27 if(a[j] - a[i] >= k && a[j] - a[i] < min) 28 min = a[j] - a[i]; 29 } 30 cout << min << endl; 31 32 return 0; 33 }
第23~29行這部分的復雜度是O(N)。整體的時間復雜度由於前面有一個排序,所以是O(NlogN)。