1. 程式人生 > >Leetcode:3Sum

Leetcode:3Sum

else 數組 tuple 次循環 時間復雜度 amp 初始 while循環 --

題目大意是傳入一個整數數組A,計算所有不同的t={A[i],A[j],A[k]},使得i,j,k互異,且A[i]+A[j]+A[k]=0。


我的思路:

定義:稱滿足條件的{A[i],A[j],A[k]}為零化三元組,且{i,j,k}稱為{A[i],A[j],A[k]}的索引,而若{A[i],A[j],A[k]}為零化三元組,則{i,j,k}也對應稱為零化索引。顯然不同的{A[i],A[j],A[k]}對應不同的{i,j,k}。若t=min{i, j, k},則稱{i, j, k}為t基零化索引,對應的若A[t]=min{A[i],A[j],A[k]},{A[i],A[j],A[k]}稱為A[t]基零化三元組。

首先利用歸並排序對數組A進行排序,花費O(nlog2n)的時間,n為A的長度。這樣我們就可以假定後續出現的數組A始終是以遞增順序排列的。

對於任意零化索引{i,j,k},不妨假設i<j<k。我們對i進行枚舉(0~n-3)。在每次枚舉中,用l和r分別記錄左右邊界,l的初始值為i+1,r的初始值為n-1。顯然在初始的情況下,可以確保所有i基零化索引{i, j, k}中,j和k必定處於[l,r]中。假設我們已知所有可能的i基零化索引{i, j, k}或者已經被我們發現,或者j和k還處於[l,r]中,那麽根據s=A[i]+A[l]+A[r]的符號,我們可以重新確定l和r的值,並保證所有i基零化索引或者已經被我們發現,或者還處於[l,r]中:

1.s < 0,則l = l + 1

2.s > 0, 則r = r - 1

3.s == 0, 則l = l + 1, r = r - 1

對上面的結論做簡單說明,當s<0時,若j == l,則必然對任意l<=t<=r,有A[i]+A[l]+A[t]<=A[i]+A[l]+A[r]<0,故能保證[l,r]不存在任何j=l的i基零化索引{i, j, k}。因此所有未被發現的存在於[l,r]中的j和k必定滿足j>l,即處於[l+1,r]中。對於情況2可以做相同分析。最後對於情況三,我們就發現了一個零化索引,而為了發現更多的零化索引,我們需要修改l和r(之所以可以同時修改l和r,是因為A[i]+A[j]+A[k]=0->A[k]=-A[i]-A[k]->A[k]=-A[i]-A[j],即三元組中任意兩個元素可以決定第三個元素,因此可以保證新的三元組必然同不可能會出現j=l或k=r的情況)。

result = empty-list

for(i = 0; i < n - 2; i = i + 1)

  if(i > 0 && A[i] == A[i - 1])

    continue

  l = i + 1, r = n - 1

  while(l < r)

    sum = A[i] + A[l] + A[r]

    if(sum < 0)

      l = l + 1

    else if(sum > 0)

      r = r - 1

    else

      insert tuple(A[l], A[j], A[r]) into result

      for(l = l + 1; l < r && A[l] == A[l - 1]; l++)

      for(r = r - 1; l < r && A[r] == A[r + 1]; r--)

上面代碼加上if(i > 0 && A[i] == A[i - 1]) continue以及for(l = l + 1; l < r && A[l] == A[l - 1]; l++)是為了跳過重復的零化三元組。一個零化三元組{A[i],A[j],A[k]}可以由其較小的兩個元素的值所唯一決定,即A[i]和A[j]的值。在i值不變的情況下,利用for(l = l + 1; l < r && A[l] == A[l - 1]; l++)可以保證下一個篩選出來的A[i]基零化三元組的第二個元素與前面篩選出來的三元組不同,而由A[i]不變和A[j]變化可以保證所有的在i階段選出的A[i]基零化三元組都互不相同。而當基不同時,顯然零化三元組也是不同的,因此保證了本段代碼中選出的零化三元組都是互不相同的。那麽如何保證在選擇跳過一部分索引基時,不會遺漏一些零化三元組呢。對於任意t>i,且A[i]=A[t],由於以基i的零化索引{i,j,k}中j和k都包含在[i+1,n-1]中,而前面也已經說明過了所有的不同的零化三元組都已經被篩選了出來,而基A[t]零化三元組中的j和k都包含在[t+1,n-1]中,考慮到[t+1,n-1]是[i+1,n-1]的子集,而A[t]+A[j]+A[k]=0->A[i]+A[j]+A[k]=0,因此能保證這些三元組都已經在對i做處理時都篩選出來了。

這段代碼分成內外兩塊叠代區域,外部for循環總共執行n-2次(定性),而內部的while循環,每次循環都必定會使得r-l至少減少1,而r-l>0,因此內部叠代發生次數必定不會超過n次,因此這段代碼的總的時間復雜度為O(nlogn)+O(n-2)*O(n)=O(n^2)。

Leetcode:3Sum