码迷,mamicode.com
首页 > 编程语言 > 详细

minheap+hashmap组合解决动态topK问题(附堆排序完整实现)

时间:2016-04-08 06:26:02      阅读:262      评论:0      收藏:0      [点我收藏+]

标签:

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

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!