• ADT:双优先队列,解决中位数问题


    ADT:双优先队列,解决中位数问题

    相关算法题:480. 滑动窗口中位数

    1 思路

    需要维护一个数据结构,能够支持

    • void insert(int num)
    • void remove(int num)
    • double getMedian()

    因为是中位数,这里用两个优先队列来实现,示例图如下:

    image-20210203220205052

    为了方便获取中位数,我们需要指定规则:

    1. 总元素个数为偶数时,大根堆的元素个数==小根堆的元素个数
    2. 总元素个数为奇数时,大根堆的元素个数==小根堆的元素个数+1

    在实现的时候为了方便,我们抽取一个void balance()来调整

    这里设计到的背景知识有:c++中优先队列的使用

    c++标准库 std::priority_queue 优先队列的使用

    C++优先队列自定义排序总结

    2 插入

    因为是基于优先队列的实现,插入很简单

    插入时,只需要比较大根堆的根,决定插入到大根堆还是小根堆即可,然后调用void balance()来进行平衡

    3 删除

    因为是基于优先队列的实现,没有提供删除接口,只能删除堆顶的元素,所以使用懒删除

    在数据结构中维护一个map,unordered_map<int, int> delay_delete;其中key为元素,value为应删除的次数

    插入时,只需要

    1. 比较大根堆的根,决定删除元素在大根堆还是小根堆
    2. 如果删除元素在堆顶,直接pop,否则懒删除,delay_delete[num]++;
    3. 调用void balance()来进行平衡

    因为使用懒删除,实现起来需要增加一些细节:

    • 使用small、big来记录堆的元素个数,而不是使用size()
    • balance()不仅需要保证两个堆的元素平衡,还需要保证顶上元素是有效的,即顶上的元素不能是懒删除的元素

    4 完整实现

    class Windows {
    private:
        // 大顶堆,存小一半的
        priority_queue<int, vector<int>, less<int>> big_heap;
        // 小顶堆,存大一半的
        priority_queue<int, vector<int>, greater<int>> small_heap;
        // 延迟删除
        unordered_map<int, int> delay_delete;
        // 总元素个数
        int n = 0;
        int small = 0;
        int big = 0;
    
        template<typename T>
        void clear_top(T &heap) {
            if(heap.empty()){
                return;
            }
            while (delay_delete[heap.top()] != 0) {
                delay_delete[heap.top()]--;
                heap.pop();
                if (heap.empty()) {
                    break;
                }
            }
        }
    
        // 保证balance以后是平衡、且顶上元素是有效的
        void balance(){
            // 调整后需要满足 big==small 或者 big==small+1
            while (big>small+1){
                clear_top(big_heap);
                int tmp = big_heap.top();
                big_heap.pop();
                small_heap.push(tmp);
                big--;
                small++;
            }
            while (big<small){
                clear_top(small_heap);
                int tmp = small_heap.top();
                small_heap.pop();
                big_heap.push(tmp);
                big++;
                small--;
            }
            clear_top(small_heap);
            clear_top(big_heap);
        }
    
    public:
        void insert(int num) {
            n++;
            if (big_heap.empty()||num <= big_heap.top()) { // 情况1:直接加在大顶堆
                big_heap.push(num);
                big++;
            } else { // 情况2:加在小顶堆
                small_heap.push(num);
                small++;
            }
            balance();
        }
    
        void remove(int num) {
            n--;
            delay_delete[num]++;
            if (num <= big_heap.top()) { // 情况1:大顶堆里删
                big--;
                if(big_heap.top()==num){
                    delay_delete[num]--;
                    big_heap.pop();
                }
            } else { // 情况2:小顶堆里删
                small--;
                if(small_heap.top()==num){
                    delay_delete[num]--;
                    small_heap.pop();
                }
            }
            balance();
        }
    
        double getMedian() {
            if(n%2==1){
                return big_heap.top();
            }else{
                return ((double)big_heap.top()+(double)small_heap.top())/2;
            }
        }
    };
    

    5 解决leetcode-408

    class Solution {
    public:
        vector<double> medianSlidingWindow(vector<int> &nums, int k) {
            vector<double> res;
            Windows windows;
            int left=0,right=k;
            for (int i = 0; i < k; ++i) {
                windows.insert(nums[i]);
            }
            res.push_back(windows.getMedian());
            while (right<nums.size()){
                windows.remove(nums[left++]);
                windows.insert(nums[right++]);
                res.push_back(windows.getMedian());
            }
            return res;
        }
    };
    
  • 相关阅读:
    Something about the "BSTR" and "SysStringLen"
    关于 i = i ++ 的问题
    duilib写个三国杀?
    关于WM_GETTEXT的应用
    hoops暂时用过的一些方法
    Hoops随便记的
    C++ win32线程数上限
    windows系统时间(SYSTEMTIME)
    Form表单提交的那些事
    多行文字溢出...
  • 原文地址:https://www.cnblogs.com/cpaulyz/p/14370044.html
Copyright © 2020-2023  润新知