TopK的解決方法通常有兩種:堆排序和partition。前者用優先隊列實現,時間複雜度爲O(NlogK)(N爲元素總數量),後者能夠直接調用C++ STL中的nth_element函數,時間複雜度O(N)。若是想獲取動態更新數據的topK就不那麼容易了,好比實時更新最常訪問的top10的網址,顯然除了維護一個size爲10的最小堆之外還須要一個哈希表實時記錄每個網址的訪問次數,並決定是否動態加入到最大堆中,同時可能刪除堆中的元素。那麼如何得到該網址在堆中的位置呢?須要另外一個hashmap記錄網址及其對應的offset。由於須要調整堆,若是直接用優先隊列實現是達不到要求的,優先隊列只能對堆頂元素進行操做,因此有必要用數組模擬堆的實現。思路仍是比較清晰的,下面上代碼!數組
typedef long URL; // URL簡化爲long struct Node { URL site; int cnt; Node(URL s, int c):site(s),cnt(c){} bool operator<(const Node& other)const{ //comparator. 注意const return this->cnt < other.cnt; } }; class WebCounter { private: unordered_map<URL, int> counterMap; //計數map unordered_map<URL, int> offsetMap; //偏移map vector<Node> minHeap; int size{1}; //初始大小爲1,idx爲0沒有存內容。兩個用處:1.offset默認爲0,表示沒有記錄;2.便於堆調整操做 int K; //top K public: WebCounter(int k):K(k){ minHeap.resize(1,{0,0}); } void work(URL url) { int curCnt = ++counterMap[url]; //更新計數 if (offsetMap[url] > 0) { //offsetMap中存在記錄說明在topK裏面 int i = offsetMap[url]; //取出偏移idx shiftDown(i); //計數增長了,可能會大於後面的數,因此shift down } else if (size <= K) { //說明堆裏面的元素數目小於k,繼續增長 minHeap.push_back(Node(url, 1)); offsetMap[url] = ++size; shiftUp(size); //增長的計數爲1,必定是最小的,因此shift up } else if(minHeap[1].cnt < current){ //size已經達到k個了,因此新元素若是大於堆頂元素就把堆頂元素替換。大於堆頂元素的狀況發生在開始已經有計數了,正好等於堆頂元素,+1以後就大於了。 minHeap[1] = Node(url, curCnt); shiftDown(1); //替換以後是第一個元素,因此只看shift down } } void shiftUp(int i) { while (i > 1 && minHeap[i] < minHeap[i/2]) { swap(minHeap[i], minHeap[i/2]); //這三行封裝起來更優雅 offsetMap[minHeap[i/2].site] = i/2; offsetMap[minHeap[i].site] = i; i>>=1; } } void shiftDown(int i) { while ((i=i*2)<=size) { if (i+1 <= size && minHeap[i+1] < minHeap[i]) { ++i; } if (minHeap[i] < minHeap[i/2]) { swap(minHeap[i], minHeap[i/2]); offsetMap[minHeap[i/2].site] = i/2; offsetMap[minHeap[i].site] = i; } else { break; } } } };
代碼中用到兩個調整堆的函數shiftDown和shiftUp,比較簡潔,另附堆排序相關的完整代碼。函數
template <class T> void swap(T& a, T&b) { T t = a; a = b; b = t; } template <class T> void shiftUp(vector<T> a, int i) { while (i > 1 && a[i/2] < a[i]) { swap(a[i], a[i/2]); i>>=1; } } template <class T> void shiftDown(vector<T> a, int size, int i) { while ((i=i*2)<=size) { if (i+1 <= size &&a[i] < a[i+1]) { ++i; } if (a[i/2] < a[i]) { swap(a[i], a[i/2]); } else { break; } } } template <class T> void makeHeap(vector<T> a, int n) { for (int i = n/2; i > 0; i--) { shiftDown(a, n, i); } } template <class T> void insert(vector<T> a, int& size, T x) { a[++size] = x; shiftUp(a, size); } template <class T> void del(vector<T> a, int& size, int i) { a[i] = a[size--]; if (i > 0 && a[i/2] < a[i]) { shiftUp(a, i); } else { shiftDown(a, size, i); } } template <class T> void heapSort(vector<T> a, int n) { makeHeap(a, n); for (int i = n; i > 1; i--) { swap(a[i], a[1]); shiftDown(a, i-1, 1); } }