雙指針



有時候我們要對一個數組進行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)。



免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM