尋找最大的K個數 zz

2021-09-30 06:36:27 字數 2996 閱讀 8153

這是程式設計之美書第2.5節的一道題目。

解法一,用nlgn複雜度的排序演算法對陣列進行從大到小排序,取前k個。但這方法做了兩件不必要做的事:它對想得到的k個數進行了排序,對不想得到的n-k個數也進行了排序。方法不可取。

解法二,用選擇排序或氣泡排序,複雜度o(nk)。但這方法也做了不必要做的一件事:對想得到的k個數進行了排序。方法不可取。

解法三,用順序統計位(類快排)演算法來計算(可參考演算法導論)。演算法導論上說這種方法從平均效能上來講是線性的,但程式設計之美上卻說複雜度是o(n*lgk)。對於這點,我對程式設計之美持懷疑態度。我認為,對於乙個無序的陣列,用順序統計位演算法,可以在近似o(n)的時間複雜度內得到第k大的元素。當這個過程執行完畢時,陣列中位於該元素之前的元素就是前k大的元素。

解法三,先查詢出陣列中的vmax和vmin,組成新的陣列[vmin,vmax],二分查詢該陣列,並在原陣列中查詢。具體請參見程式設計之美。複雜度為o(nlgn),方法不可取。

解法四,維護乙個k大小的陣列a,遍歷原陣列中的每個數,與a每個元素進行比較,如果大於a陣列中的最小元素,則交換,否則繼續。複雜度為o(nk),方法不可取。

解法五,維護乙個最小堆,大小為k。一開始先從原陣列中隨便取k個元素建最小堆,複雜度為o(k)。然後遍歷原陣列中剩餘的n-k個元素,每個元素先與堆頂比較,如果大於堆頂則交換,並維護最小堆性質。總複雜度為k+(n-k)lgk = nlgk。該方法只需要遍歷一次陣列,且無須在記憶體中儲存所有陣列資料,而只需維護k大小的資料。是一種適用於 大資料,小記憶體的好方法。如果k還是太大,則分次來求,通過先用k』(k』解法六,利用hash儲存陣列中元素si出現的次數,利用計數排序的思想,線性從大到小掃瞄過程中,前面有k-1個數則為第k大數,平均情況下時間複雜度o(n)。這個方法要求陣列中的取值範圍跨度不能太大。

解法七,對於原陣列a,它的取值範圍跨度為[vmin,vmax],直接用hash來統計的方法就是解法六。這裡,我們把該範圍平均分成m份,每份範圍跨度都是n/m。遍歷原陣列,分別統計各個跨度中每個元素出現的次數。從[vmax-n/m, vmax] 遍歷到 [vmin, vmin+n/m],累加元素出現的次數。當發現某個跨度累加超過k時,那麼我們就知道第k大元素在這個跨度裡。這樣,我們就可以在該跨度裡用hash的方法尋找該元素(該跨度很小,可以在記憶體中存放hash表)。程式設計之美中的時間複雜度有待研究。

解法八,有文章表明,用「敗者樹」有可能比堆得到更好的結果。參見[6]。是乙個北交大的學生寫的。

解法一、二、四最差,不推薦。

解法三、解法四 只適用於小資料,因為他們要經常讀取整個陣列的資料。解法四不推薦。

解法三、解法五是線性的解法,分別適用於小資料和大資料。 【因為lgk可能很小,堆方法類線性】 ,推薦。

解法六、解法七都是線性的解法,在面對大資料量時用解法七可以比較高效。

解法八是乙個很有意思的解法。

1. 找前k個不同的浮點數。

網上[2]有關該題的簡短解答。他認為上面的各種解法都可以應用在本題上。但我認為並不可以這樣來做。因為本題是想找「不同」的前k大數。用堆排序,中位數方法無法計算重複的資料。所以,個人認為可以用兩種方法:

a. 完全排序。然後順序找前k個「不同」的元素。

b. 用桶排序的方法。重複的資料被對映到桶中,只算乙個。

還要注意的是,浮點數的相同應該用fabsf(a-b)<0.00001等類似的方法來判斷。

2. 如果是找第k到第m(0同[2]的中解答。

3. 在搜尋引擎中,網路上的每個網頁都有「權威性」權重,如page rank。如果我們需要尋找權重最大的k個網頁,而網頁的權重會不斷地更新,那麼演算法要如何變動以達到快速更新(incremental update)並及時返回權重最大的k個網頁?

用對映二分堆的方式。用o(4*n)的方法對原數組建最大堆,然後pop出k次即可。時間複雜度為o(4*n + k*logn)。對映二分堆與普通堆不同的地方是:它的節點並不真正儲存資料單元本身,而是儲存指向資料單元的指標。因此 當需要交換父子節點的資料時,可以避免拷貝大量資料所消耗的時間。同時,對映二分堆還有乙個功能可以根據具體的資料單元的索引來刪除該單元,即使這個單元 不是堆中的最值。[4].

4.在實際應用中,還有乙個「精確度」的問題。我們可能並不需要返回嚴格意義上的最大的k個元素,在邊界位置允許出現一些誤差。當使用者輸入乙個query的時候,對於每乙個文件d來說,它跟這個query之間都有乙個相關性衡量權重f (query, d)。搜尋引擎需要返回給使用者的就是相關性權重最大的k個 網頁。如果每頁10個網頁,使用者不會關心第1000頁開外搜尋結果的「精確度」,稍有誤差是可以接受的。比如我們可以返回相關性第10 001大的網頁,而不是第9999大的。在這種情況下,演算法該如何改進才能更快更有效率呢?網頁的數目可能大到一台機器無法容納得下,這時怎麼辦呢?

參考[2]. 正如提示中所說,可以讓每台機器返回最相關的k』個文件,然後利用歸併排序的思想,得到所有文件中最相關的k個。 最好的情況是這k個文件在所有機器中平均分布,這時每台機器只要k』 = k / n (n為所有機器總數);最壞情況,所有最相關的k個文件只出現在其中的某一台機器上,這時k』需近似等於k了。我覺得比較好的做法可以在每台機器上維護一 個堆,然後對堆頂元素實行歸併排序。個人覺得這還是正確的解答。沒有回答90%準確時的高效率做法。如果是用維護堆的做法,這是完全精確的。

5. 如第4點所說,對於每個文件d,相對於不同的關鍵字q

1, q

2, …, qm

,分別有相關性權重f(d, q

1),f(d, q

2), …, f(d, qm

)。如果使用者輸入關鍵字qi

之後,我們已經獲得了最相關的k個文件,而已知關鍵字**

跟關鍵字qi

相似,文件跟這兩個關鍵字的權重大小比較靠近,那麼關鍵字qi

的最相關的k個文件,對尋找**

最相關的k個文件有沒有幫助呢?

參考[2]. 肯定是有幫助的。在搜尋關鍵字**

最相關的k個文件時,可以在**

的「近義詞」相關文件中搜尋部分,然後在全域性的所有文件中在搜尋部分。

[1]. csdn上的討論

[2]. 別人的想法:

[3]. 另種想法

[4].

[5]. 對映二叉堆

[6]. 敗者樹

尋找最大的K個數

方法一 改進的快速排序 分割槽時,根據數p將陣列分為兩部分,設大於p的數個數為a,小於p的數的個數為b。如果,a k,則從這a個數取最大的k個數,若a時間複雜度是o nlogk include includevoid swap float a,float b int fun float n,int ...

尋找最大的K個數

程式設計之美有一道考察多種排序的題目,題目如下 有乙個長度為n的無序陣列,假定其中的每乙個元素都各不相等,求其中最大的k個數。作者對於此題目結合各種排序演算法給出了五種解法思路。解法一 使用快速排序或堆排序對它們元素進行排序,整個排序的時間複雜度為o n lo g2n 然後取出前k個,時間複雜度為o...

尋找最大的K個數

前提條件 有n個無序的數,假定它們各不相等,如何選出其中最大的若干個數 適用於元素數量不多,記憶體中可儲存整個陣列序列。通過快速排序或堆排序對陣列排序,時間複雜度為o n log2n 然後取出前k個數,時間複雜度為o k 總時間複雜度為o n log2n o k 進一步的,可以知道,我們只需要前 k...