703. 数据流中的第 K 大元素(面试题警告)优先队列+堆

设计一个找到数据流中第 k 大元素的类(class)。注意是排序后的第 k 大元素,不是第 k 个不同的元素。

请实现 KthLargest 类:

KthLargest(int k, int[] nums) 使用整数 k 和整数流 nums 初始化对象。
int add(int val) 将 val 插入数据流 nums 后,返回当前数据流中第 k 大的元素。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/kth-largest-element-in-a-stream

输入:
[“KthLargest”, “add”, “add”, “add”, “add”, “add”]
[[3, [4, 5, 8, 2]], [3], [5], [10], [9], [4]]
输出:
[null, 4, 5, 5, 8, 8]
解释:
KthLargest kthLargest = new KthLargest(3, [4, 5, 8, 2]);
kthLargest.add(3); // return 4
kthLargest.add(5); // return 5
kthLargest.add(10); // return 5
kthLargest.add(9); // return 8
kthLargest.add(4); // return 8

class KthLargest{
    PriorityQueue<Integer> pq;
    int k;
    public KthLargest(int k, int[] nums){
        this.k=k;//初始化队列存放的数量
        pq=new PriorityQueue<Integer>();//初始化队列
        for(int x:nums){//逐个添加,只保留前k大的元素,按照升序添加进队列
            add(x);
        }
    }
    public int add(int val){
        pq.offer(val);//add添加失败会报错,offer只返回false
        if(pq.size()>k){
            pq.poll();//弹出队首值
        }
        return pq.peek();//返回队首值
    }

}

本题使用优先队列非常合适

优先队列PriorityQueue是Queue接口的实现,可以对其中元素进行排序,
可以放基本数据类型的包装类(如:Integer,Long等)或自定义的类
对于基本数据类型的包装器类,优先队列中元素默认排列顺序是升序排列

peek()//返回队首元素
poll()//返回队首元素,队首元素出队列
add()//添加元素
size()//返回队列元素个数
isEmpty()//判断队列是否为空,为空返回true,不空返回false
我们可以使用一个大小为 kk 的优先队列来存储前 kk 大的元素,其中优先队列的队头为队列中最小的元素,也就是第 kk 大的元素。

在单次插入的操作中,我们首先将元素val 加入到优先队列中。如果此时优先队列的大小大于 k,我们需要将优先队列的队头元素弹出,以保证优先队列的大小为 k。

优先队列的实现原理就是小顶堆,那我们手动实现一个(多加分啊)

class KthLargest{
    int[] heap;//维护小根堆,小于等于堆顶丢弃,否则插入堆顶后调整,把最小的删掉
    int size=0;
    int count=0;
    public KthLargest(int k,int[] nums){
        heap=new int[k];
        count=k;
        for (int i:nums) {
            add(i);
        }
    }
    public int add(int val){
        if(size<count){//堆内元素数量小于容量,添加元素并调整堆
            heap[size]=val;
            up(size);//自下向上调整
            size++;
        }else if(heap[0]<val){//堆满,堆顶元素小于待插入元素,弹出堆顶元素并插入新元素调整
            heap[0]=val;
            down(0);//自上向下调整
        }
        return heap[0];//返回堆顶最小值
    }
    public void down(int u){
        int t=u;
        if(2*u+1<size&&heap[2*u+1]<heap[t]){
            t=2*u+1;//左孩子比较小
        }
        if(2*u+2<size&&heap[2*u+2]<heap[t]){
            t=2*u+2;//右孩子比较小
        }
        if(t!=u){//有调整必要
            int temp=heap[u];
            heap[u]=heap[t];
            heap[t]=temp;//将小的节点调整上去
            down(t);//递归调整下面的节点
        }
    }
    public void up(int u){
        while((int)(Math.ceil(u/2.0)-1)>=0&&heap[(int)Math.ceil(u/2.0)-1]>heap[u]){//有父节点并有调整必要
            int p=(int)Math.ceil((u/2.0)-1);//父节点
            int temp=heap[u];
            heap[u]=heap[p];
            heap[p]=temp;
            u=p;//要调整节点变成了其父节点
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值