《編程珠璣》第十四章的一個問題是:
在具有10億個數值的文件中找出最大的100萬個數組。
使用堆解決這個問題的思路
1. 取10億個數值中的最初的100萬個,放到一個堆中,初始化這個堆爲小頂堆。
2. 依次取剩下的每個數值,用取出的這個值和小頂堆的最小值比較:
如果大於最小值,用這個值替換小頂堆的最小值,調整堆使得它還是小頂堆。
如果小於或者等於最小值,則繼續取下一個值。
最後這個堆包含的就是100萬個最大值。
這個問題簡單推廣就是:尋找一個無序集合中前n個最大或者最小的值。也等價於:尋找一個無序集合中理解。這個思路好理解。嘗試嚴格證明這個思路。
M是一個集合,要取其中的前n個最大值。
假設A是初始的n個元素組成的堆,則剩下的元素組成集合X=M-A。假想存在一個集合B,它存放X中的元素和A中最小值比較後的較小的值。對一個元素x(i)(屬於X, i表示第i個元素),它和A的最小值比較,如果x(i)大,則把x(i)放入A,把A的最小值放入假想的集合B中(實際沒有放入,假想),記此時的A爲A(i),B爲B(i)。假設X中元素有N個,則A,B對應的變化是:
A(0),A(1),…,A(N-1),A(N)
B(0),B(1),…,B(N-1),B(N)
A(N)的元素就是前n個最大的值。
我們需要證明的就是任意一個屬於B(N)的元素,一定不大於A(N)中的任意一個元素,或者說一定不大於A(N)中的最小值。
證明:
假設元素b屬於B(N),則b來自於A中,或者來至於X中。
如果b來至於A中,則b一定是某次A的最小值,然後被X中的一個比它大的值替換。則一定存在A(j-1)(0<=j<=N-1),滿足b=Min(A(j-1)),同時b<Min(A(j))。我們知道每次A變化時,一定是一個較大的值替換了最小的值,所以Min(A(0))<=Min(A(1))<=…<=Min(A(N))。所以b<Min(A(j))<=Min(A(N))。
如果b來至於X中,則一定是從X中取出b和對應A(j)比較時,b<=Min(A(j))。所以b<=Min(A(j))<=Min(A(N))。
所以B(N)中的任意一個元素不大於A(N)的最小元素。