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); } }