有N個數的數組,找出這個數組中的兩個數,使得這兩個數的和最接近0


有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>的狀態,而我們采用的方法又是不斷記錄最小值,那么我們肯定能夠得到最優解。

 

三樓給出了一個非常簡便的方法:按照絕對值排序。仔細想想,其實就是我上面的方法的另一種形式。

比如,

-7 -2 3 4 5 9

按照3樓的方法,將會是 -2 3 4 5 7 9取相鄰兩數值差絕對值的最小值。如果我們按照絕對值由大到小排序,則序列為9 7 5 4 3 -2,那么這時取相鄰兩個數的絕對值等價於方法二的兩個游標。


免責聲明!

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



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