标签:
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); } }
minheap+hashmap组合解决动态topK问题(附堆排序完整实现)
标签:
原文地址:http://www.cnblogs.com/sweet-coffee/p/5366294.html