数据流中的第 K 大元素
设计一个找到数据流中第 k 大元素的类(class)。注意是排序后的第 k 大元素,不是第 k 个不同的元素。
请实现 KthLargest 类:
KthLargest(int k, int[] nums) 使用整数 k 和整数流 nums 初始化对象。
int add(int val) 将 val 插入数据流 nums 后,返回当前数据流中第 k 大的元素。
示例:
输入:
[“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
解题思路
如果该题使用数组的话,当每次调用 add() 函数时,向数组中添加一个元素,然后调用 sort() 函数进行排序,返回排序后数组的第 K 个数字。该做法在每次调用 add() 函数时的时间复杂度为 O(K*log(K)),该时间复杂度太高,当 K 很大且 add() 调用次数太多的时候,一定会超时。
使用数组最大的问题:数组无法在不调用 sort() 函数的情况下自动排序,这会导致时间复杂度过高。
有什么数据结构能够自带排序功能呢?答案是:堆
- 使用大小为 K 的小根堆,初始化时,要使堆中的元素个数不超过 K 个。
- 在每次调用 add() 函数时,要将新元素 val 添加到堆中(此时,堆会自动的排序,形成小根堆),如果此时堆中的元素个数超过了 K 个,则要把堆中的最小元素(堆顶)从当前堆中拿出来。
- 此时堆中的最小元素(堆顶)就是整个数据流中的第 K 大的元素(因为小根堆中保留的一直是堆中的前 K 大的元素,而此时堆的大小为 K,所以堆顶元素就是整个数据流中第 K 大元素)。
代码
class KthLargest {
//小根堆
/*
PriorityQueue是基于优先堆的一个无界队列,
这个优先队列中的元素可以默认自然排序或者通过提供的Comparator(比较器)在队列实例化的时排序。
用优先队列可实现大根堆和小根堆(默认是小根堆)
*/
PriorityQueue<Integer> pq;//声明
int k;
public KthLargest(int k, int[] nums) {
this.k = k;
pq = new PriorityQueue<Integer>();//分配内存
//把nums数组遍历一遍
for (int x : nums) {
add(x);
}
}
//添加函数
public int add(int val) {
pq.offer(val);//offer函数将给定元素(val)添加到此PriorityQueue中
if (pq.size() > k) {
/*
对于poll()方法,它将检索队列的head元素,然后删除队列的head元素。
如果队列为空,则它将返回null,但不会引发异常。
*/
pq.poll();
}
//用于返回第一个元素,而不从此PriorityQueue中删除一个元素。
return pq.peek();
}
}
时间复杂度:O(n * logk),其中 n 为初始化时 nums 的长度。
空间复杂度:O(k),需要使用优先队列存储前 k 大的元素。