【前言】在大規模資料處理中,常遇到的一類問題是,在海量資料中找出出現頻率最高的前k個數,或者從海量資料中找出最大的前k個數,這類問題通常稱為「topk」問題
【解決思路】
針對topk類問題,通常比較好的方案是【分治+trie樹/hash+小頂堆】,即先將資料集按照hash演算法分解成多個小資料集,然後使用trie樹或者hash表統計每個小資料集中的query詞頻,之後用小頂堆求出每個資料集中出頻率最高的前k個數,最後在所有top k中求出最終的top k。
實際上,最優的解決方案應該是最符合實際設計需求的方案,在實際應用中,可能有足夠大的記憶體,那麼直接將資料扔到記憶體中一次性處理即可,也可能機器有多個核,這樣可以採用多執行緒處理整個資料集。
【解決】在得到資料後,如何對獲得的資料進行topk排序呢。下面簡單介紹幾種常見方法。
基於partition(分割槽)解決topk min的問題:根據陣列的第k個數字來調整,使得比第k個數小的都在陣列的左邊,比第k個資料大的所有數字在陣列的右邊。
/**
* 最小的k個數,o(n)解法,需要修改原陣列
* @param a
* @param n
* @param b
* @param k
*/public void topkmin(int a,int n,int b,int k)
for(int i=0;i=pivot)
right--;
if(left
複雜度分析:先用hash表統計每個query出現的次數,o(n);然後第二步、採用堆資料結構找出top 10,n*o(logk)。所以,我們最終的時間複雜度是:o(n) + *o(n1*logk)。(n為資料總數,n1為不重元素個數)。
@test
public void testbuildminheap();
int k=3;
int topk=new int[k];
for(int i=0;iroot)
} //buildminheap(a,a.length);
system.out.println(arrays.tostring(topk));
} public void heapfiy(int a,int i,int len)
}
基於優先順序佇列來實現的topk問題:實現comparator介面,使得佇列元素按照公升序/降序排序,維護乙個長度為k的陣列,把佇列的元素出隊放到陣列中即可。
/*
隊尾
|1 |3
|2 |2
|3 |1
隊頭降序 公升序
*/
public static int topklargest(int input, int k) else if(o1 > o2) else
}});
for (int i : input)
int out = new int[k];
for (int i = 0; i < out.length; i++)
return out;
} public static int topksmallest(int input, int k) else if(o1 > o2) else
}});
for (int i : input)
int out = new int[k];
for (int i = 0; i < out.length; i++)
return out;
} public static void main(string args) , 3);
system.out.print("largest: ");
for (int o : out)
out = topksmallest(new int , 3);
system.out.println();
system.out.print("largest: ");
for (int o : out)
}
【參考資料】
《十道海量資料處理面試題與十個方法大總結》:
Top K問題 基於快速排序
p為待查詢陣列,l,r分別為陣列下標,k表示第k大數 public intfindkth int p,int l,int r,int k 一次快速排序 以p l 為比較物件,比p l 大或等於的在其左邊,否則在其右邊 public intquicksort int p int l,int r whi...
堆排序與topK問題
找出乙個有10億數字陣列中,前k個最大值 第一步 hash去重 解法1 劃分法 def partition l,left,right low left if left right key l left high right while low high while low high and l hi...
堆排序 TOPK問題 C
現在有n個數,設計演算法得到前k大的數。k 首先從原列表擷取前k個元素,組成乙個新列表。然後將這個新列表構建成乙個小根堆,那麼根就新列表中最小的數。接著繼續從原列表取出k以後的元素,和小根堆根比對,如果比根小,那麼肯定不屬於前k大的數,如果比根大,替換根,然後做一次向下調整,根重新變成新列表最小的數...