题目:
设计一个找到数据流中第K大元素的类(class)。注意是排序后的第K大元素,不是第K个不同的元素。
你的 KthLargest 类需要一个同时接收整数 k 和整数数组nums 的构造器,它包含数据流中的初始元素。每次调用 KthLargest.add,返回当前数据流中第K大的元素。
示例:
int k = 3;
int[] arr = [4,5,8,2];
KthLargest kthLargest = new KthLargest(3, arr);
kthLargest.add(3); // returns 4
kthLargest.add(5); // returns 5
kthLargest.add(10); // returns 5
kthLargest.add(9); // returns 8
kthLargest.add(4); // returns 8
解题思路:
利用优先队列的方式,即用一个二叉堆(最小堆),保留的是前k大的元素,则堆顶则是我们要求的第k大元素。将数据流加入优先队列中,每次判断堆的元素是否等于k,如果等于k,在不需要加入元素。
当加入的元素比堆顶的元素还要大,则表示堆顶的元素不是前k的元素中的第k大的元素,则将堆顶元素移除,然后将新加入的元素加入堆中,经过自我调整后,此时堆顶的元素即可第k大的元素。
图解:
假设数组为:{1,3, 5, 6, 8},则第k大假设k=4,即第四大元素,则我们此时的堆如下:
此时,当数据流继续加入元素4,则此时数组为{1,3, 5, 6, 8, 4},则4大于堆顶元素,则我们要将堆顶元素3移除,并且将4插入到堆中去。
删除操作:
将3跟最后一个元素 交换
最小堆的自我调整,将8进行“下浮”操作
删除完毕
插入操作:
将插入元素4放到堆中的最后一个元素
将4进行上浮操作:
此时,4则是我们额第k大的元素
代码:
/**
* 描述: 优先队列题目: 寻找数据流中的第K大元素
* 思路: 基于最小堆实现
*
* @author pengjie_yao
* @date 2019/7/18 21:20
*/
public class PriortyQueueByK {
final PriorityQueue<Integer> priorityQueue;
final int k;
public PriortyQueueByK(int k, int[] array) {
this.k = k;
priorityQueue = new PriorityQueue<>(k);
for (int i : array) {
add(i);
}
}
/**
* 新增堆顶元素
* @param n
* @return
*/
public int add(int n) {
// 堆的元素个数小于k个元素的时候,加入堆
if (priorityQueue.size() < k) {
priorityQueue.offer(n);
} else if (priorityQueue.peek() < n) {
// 堆顶的元素小于要加入的元素,则表示堆顶的元素不是第k大的元素,则将堆顶元素去掉,新元素加入
priorityQueue.poll();
priorityQueue.offer(n);
}
// 返回堆顶元素
return priorityQueue.peek();
}
public static void main(String[] args) {
int[] array = new int[]{1, 2, 3, 5, 4, 20, 15};
PriortyQueueByK priorityQueue = new PriortyQueueByK(1, array);
// return 21
priorityQueue.add(21);
// return 22
priorityQueue.add(22);
// return 34
priorityQueue.add(34);
}
}