有N個數的數組,沒有順序。現在的問題是讓你在數組中找出兩個數,使得這兩個數的和儘可能的接近0。
想到的的方法是嘗試所有數對<xi,xj>的組合,之後找出其中和的絕對值最小的數對即可。但是這樣做的時間複雜度是O(N^2),有沒有更快一點的方法呢?
這裡給出一個O(NlogN)時間複雜度的演算法。
有一種比較直觀的做法。
對數組排好序之後。如果數字全部是正數,那麼取最小的兩個數的和。如果數字全部是負數,則取最大的兩個數位和。如果數字有正有負。那麼我們必須枚舉每一個xi,之後用二分在數組中找小於等於-xi的最大值和大於等於-xi的最小值。能與xi構成和的絕對值最小的數字,肯定就是這兩個數字中的某一個。所有的xi都枚舉過之後,我們就可以找出和的絕對值最小值了。時間複雜度是O(NlogN)。
還有一種比較簡便的做法,但是證明有些複雜。
1:對數組中的數進行從小到大排序。
2:設定兩個遊標,一個指向第一個數,另一個指向最後一個數,之後進行如下操作:把遊標處的兩個數相加取絕對值,與記錄當前已經得到的最小絕對值比較,小則記錄。之後對遊標處的數的絕對值進行比較,移動指向數字絕對值較小的遊標(如果是左面的遊標指向的數字絕對值小,則向右移動,否則右面的遊標向左移動)。迴圈此步驟,直至找到最小絕對值0或者遊標相遇。
舉個例子:原始序列:-2 3 4 -7 9 5排序後:-7 -2 3 4 5 9尋找過程:res = MAX_INF;-7 -2 3 4 5 9| | res = 2-7 -2 3 4 5 9| | res = 2-7 -2 3 4 5 9 | | res = 2-7 -2 3 4 5 9 | | res = 2-7 -2 3 4 5 9 | | res = 1-7 -2 3 4 5 9 | res = 1
雖然給出了這樣的演算法,但是怎麼證明該演算法是正確的?以下的證明很繁瑣。
證明:
設排好序的序列為
x[1],x[2],......,x[i],x[i+1],......,x[j],x[j+1],......,x[n-1],x[n]
假設最優解為<x[i],x[j]>
x[1],x[2],......,x[i],x[i+1],......,x[j],x[j+1],......,x[n-1],x[n]
| |
如果演算法是正確的,那麼必須得證明演算法執行過程中遊標不會錯過最優解。
初始時,遊標為<1, n>,顯然沒有錯過最優解。之後每步,都有一個遊標移動。那麼肯定有一個遊標會先到達最優位置。例如左面的遊標最先到達x[i]的位置,而另一個遊標的位置為k>j
x[1],x[2],....,x[i],x[i+1],....,x[j],x[j+1],....x[k],....,x[n-1],x[n]
| |
下面分情況討論:
(1): x[i] > 0
這種情況下,顯然 x[j] > 0 , x[k] > 0,並且x[i]+x[k] > x[i]+x[j],那麼把右面的遊標向左移動會更優,並且不會錯過最優解。
(2): x[i] < 0,x[j] > 0
這種情況下又分3中小情況。
2.1: x[i]+x[j] > 0
這種情況下,x[i]+x[k] > 0 並且x[i]+x[j] < x[i]+x[k] (因為x[j]<x[k]),那麼把右面的遊標向左移動會更優,並且不會錯過最優解。
2.2: x[i]+x[j] < 0, x[i]+x[k] < 0
這種情況下,顯然|x[i]+x[k]| < |x[i]+x[j]|,推出<x[i],x[j]>不是最優解,這與假設相矛盾,所以在遇到最優解之前,這種情況不會出現。
2.3: x[i]+x[j] < 0, x[i]+x[k] > 0
這種情況下有|x[i]+x[j]| < x[i]+x[k]=>-x[i]-x[j]<x[i]+x[k]=>x[j]+x[k] > -2*x[i] => x[k] > -x[i] = |x[i]| (因為x[j] < -x[i])。也就是說,在這種情況下,我們會移動位置為k那個遊標,而不會移動位置為i那個遊標,所以不會錯過最優解。
(3): x[i] < 0, x[j] < 0
這種情況下又繼續分成兩種小情況。
3.1 x[k] < 0
這種情況下<x[i],x[j]>不是最優解,這與假設相矛盾,所以在遇到最優解之前這種情況不會出現。
3.2 x[k] > 0
這種情況下也分兩種小情況。
3.2.1 x[i]+x[k] < 0
|x[i]+x[j]| < |x[i]+x[k]| => -x[i]-x[j] < -x[i]-x[k] => x[j] > x[k],這與x[j] < x[k]相矛盾,因為x是排好序的數組。所以這種情況在遇到最優解之前不會出現。
3.2.2 x[i]+x[k] > 0
|x[i]+x[j]| < |x[i]+x[k]| => -x[i]-x[j] < x[i]+x[k] => x[k]+x[j] > -2*x[i]=> x[k] > -2*x[i]-x[j] => x[k] > |x[i]|。也就是說,在這種情況下,我們會移動位置為k那個遊標,而不會移動位置為i那個遊標,所以不會錯過最優解。
由對稱性可證明當右面的遊標先到達x[j]時這種情況。
綜上所述可知,只要重複演算法的過程,那麼我們的遊標肯定能夠到達最優解<i,j>的狀態,而我們採用的方法又是不斷記錄最小值,那麼我們肯定能夠得到最優解。