9. Pattern: Two Heaps,双堆类型

目录

经典力扣题

295. 数据流的中位数Find the Median of a Number Stream (medium)

480. 滑动窗口中位数Sliding Window Median (hard)

Maximize Capital (hard)

力扣题


很多问题中,我们被告知,我们拿到一大把可以分成两队的数字。为了解决这个问题,我们感兴趣的是,怎么把数字分成两半?使得:小的数字都放在一起,大的放在另外一半。双堆模式就能高效解决此类问题。

正如名字所示,该模式用到了两个堆,是不是很难猜?一个最小堆用来找最小元素;一个最大堆,拿到最大元素。这种模式将一半的元素放在最大堆中,这样你可以从这一堆中秒找到最大元素。同理,把剩下一半丢到最小堆中,O(1)时间找到他们中的最小元素。通过这样的方式,这一大堆元素的中位数就可以从两个堆的堆顶拿到数字,从而计算出来。

判断双堆模式的秘诀:

  • 这种模式在优先队列,计划安排问题(Scheduling)中有奇效
  • 如果问题让你找一组数中的最大/最小/中位数
  • 有时候,这种模式在涉及到二叉树数据结构时也特别有用

 

经典力扣题

295. 数据流的中位数Find the Median of a Number Stream (medium)

class MedianFinder {

    private PriorityQueue<Integer> maxheap;
    private PriorityQueue<Integer> minheap;

    /** initialize your data structure here. */
    public MedianFinder() {
        maxheap=new PriorityQueue<>((x,y)->y-x);//java中PriorityQueue是小顶堆,小数在前,我们让其逆序即为大顶堆
        minheap=new PriorityQueue<>();
    }
    

    //让这个数在大顶堆中走一下,再将大顶堆中最大的那个数入小顶堆,这个逻辑会导致奇数时maxheap的size小于minheap,所以要写判断语句奇数情况下要还一个回去
    public void addNum(int num) {
        maxheap.add(num);
        minheap.add(maxheap.poll());
        if((maxheap.size()+minheap.size())%2==1){
            maxheap.add(minheap.poll());
        }
    }
    
    public double findMedian() {
        if((maxheap.size()+minheap.size())%2==1){
            return maxheap.peek();
        }else{
            return ((double)maxheap.peek()+minheap.peek())/2;
        }
    }
}

480. 滑动窗口中位数Sliding Window Median (hard)

错误解法

原因:维护窗口需要删除left元素,这在堆中十分困难,而本解法用maxheap.remove(nums[left-1]),错误理解了remove的含义

//暴力:每次对窗口进行排序
//双堆
class Solution {
    public double[] medianSlidingWindow(int[] nums, int k) {

        int len=nums.length;
        double[] ans=new double[len-k+1];
        

        PriorityQueue<Integer> maxheap=new PriorityQueue<> ((x,y)->y-x);//大顶堆,大数在前
        PriorityQueue<Integer> minheap=new PriorityQueue<> ();//小顶堆
        //大顶堆中的数都比小顶堆的小,且大顶堆的size大于等于小顶堆

        boolean x=(maxheap.size()+minheap.size())%2==0;

        int left=0,right=k-1;
        for(int i=0;i<k-1;i++){
            maxheap.add(nums[i]);
            minheap.add(maxheap.poll());
            maxheap.add(minheap.poll());
        }

        while(right<len){
            if(left!=0){
                if(nums[left-1]<=ans[left-1]){//说明在大顶堆
                    maxheap.remove(nums[left-1]);
                }else{
                    minheap.remove(nums[left-1]);//移除left那个数没有这么简单,这个函数不是这么用的
                    if(maxheap.size()>minheap.size()+1){
                        minheap.add(maxheap.poll());
                    }
                }
                maxheap.add(nums[right]);
                minheap.add(maxheap.poll());
                maxheap.add(minheap.poll());
            }
            


            double med=0;
            if(x){
                med=(double)(maxheap.peek()+minheap.peek())/2;
            }else{
                med=maxheap.peek();
            }
            ans[left]=med;
            left++;
            right++;
        }

        return ans;
    }
}

正确解法

延迟删除:在 295 题中,我们使用两个堆寻找中位数,如果两个堆是平衡的,那么我们可以从堆顶元素得到中位数。在本题中,这个性质同样成立,即使堆中有多余的元素(即应当被移除的元素),但只要两个堆是平衡的,我们仍然可以从堆顶得到中位数。我们可以使用哈希表来标记所有被移除的无效元素,如果某个堆的堆顶是一个无效元素,我们才会把它删除。

Maximize Capital (hard)

力扣题

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值