zoukankan      html  css  js  c++  java
  • 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;
        }
    };
    
  • 相关阅读:
    maven/gradle 打包后自动上传到nexus仓库
    idea中怎么忽略(ignore)掉 .idea等文件
    MySQL优化一例
    微信调用照相拍照等 js 接口的权限配置 和 照片上传和下载实现
    {"errcode":40097,"errmsg":"invalid args hint: [vjNe7xxxxxx8vr19]"}——记录一次微信错误处理
    jsmooth compilation failed error null
    java.lang.UnsatisfiedLinkError: %1 不是有效的 Win32 应用程序。
    IE8 ajax缓存问题
    com.sun.xml.internal.ws.server.ServerRtException: Server Runtime Error: java.net.BindException: Cannot assign requested address: bind
    chrome 浏览器的预提取资源机制导致的一个请求发送两次的问题以及ClientAbortException异常
  • 原文地址:https://www.cnblogs.com/cpaulyz/p/14370044.html
Copyright © 2011-2022 走看看