問題描述:有N(N>>10000)個整數,求出其中的前K個最大的數。(稱作Top k或者Top 10)
問題分析:由於(1)輸入的大量數據;(2)只要前K個,對整個輸入數據的保存和排序是相當的不可取的。
可以利用數據結構的最小堆來處理該問題。
最小堆如圖所示,對於每個非葉子節點的數值,一定不大於孩子節點的數值。這樣可用含有K個節點的最小堆來保存K個目前的最大值(當然根節點是其中的最小數值)。
每次有數據輸入的時候可以先與根節點比較。若不大於根節點,則舍棄;否則用新數值替換根節點數值。並進行最小堆的調整。
實現代碼以及說明:
#include<stdio.h> int n; ///數字個數,n很大(n>10000) int dui[10]; #define K 10 ///Top K,K的取值 void create_dui(); ///建堆 void UpToDown(int); ///從上到下調整 int main() { int i; int tmp; while(scanf("%d",&n)!=EOF) { for(i=1;i<=K;i++) ///先輸入K個 scanf("%d",&dui[i]); create_dui(); ///建小頂堆 for(i=K+1;i<=n;i++) { scanf("%d",&tmp); if(tmp>dui[1]) ///只有大於根節點才處理 { dui[1]=tmp; UpToDown(1); ///向下調整堆 } } } return 1; } void create_dui() { int i; int pos=K/2; ///從末尾數,第一個非葉節點的位置K/2 for(i=pos;i>=1;i--) UpToDown(i); } void UpToDown(int i) { int t1,t2,tmp,pos; t1=2*i; ///左孩子(存在的話) t2=t1+1; ///右孩子(存在的話) if(t1>K) ///無孩子節點 return; else { if(t2>K) ///只有左孩子 pos=t1; else pos=dui[t1]>dui[t2]? t2:t1; if(dui[i]>dui[pos]) ///pos保存在子孩子中,數值較小者的位置 { tmp=dui[i];dui[i]=dui[pos];dui[pos]=tmp; UpToDown(pos); } } }
由於僅僅保存了K個數據,有調整最小堆的時間復雜度為O(lnK),因此TOp K算法(問題)時間復雜度為O(nlnK).