设计一个找到数据流中第 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;//要调整节点变成了其父节点
}
}
}