我们首先来看看这道题目,找到数据流中第K大的元素:
首先拿到这道题目,我们有可能会想到一种暴力的解法,就是首先把输入的数据流进行排序,然后从排好序的数据流中找到第K个元素,就行了,这也是一种解法,我们可以计算一下这种解法情况下的时间复杂度,假设有
N
N
N 个元素,要将
K
K
K 个元素进行快速排序,那么时间复杂度就为:
O
(
N
⋅
K
⋅
log
K
)
O(N \cdot K \cdot \log K)
O(N⋅K⋅logK)。
我们有没有更好的办法来解决这个问题呢?这里我们可以用到优先队列的数据结构,优先队列的作用是能保证每次取出的元素都是队列中权值最小的(Java的优先队列每次取最小元素,C++的优先队列每次取最大元素),其通过堆实现,具体说是通过完全二叉树(complete binary tree)实现的小顶堆(任意一个非叶子节点的权值,都不大于其左右子节点的权值)。我们可以将优先队列的大小定义成
K
K
K,堆顶就存放的是
K
K
K 个元素中最小的那个,当有新的数据流来的时候,就和这个堆顶最小的元素进行比较,比这个堆顶元素小的话,不放进优先队列中;如果新的数据流比堆顶的元素大的话,那么把堆顶的元素剔除,把数据流中的元素加入优先队列。所以说用大小为
K
K
K 的优先队列的话,可以保证堆顶一定是第K个大小的元素。这个时候的时间复杂度为:
O
(
N
⋅
log
K
)
O(N \cdot \log K)
O(N⋅logK),所以说用优先队列的方法来解决这道题目是最优的方法。代码如下所示:
class KthLargest {
private PriorityQueue<Integer> queue;
private int k = 0;
public KthLargest(int k, int[] nums) {
queue = new PriorityQueue(k);
this.k = k;
for(int i = 0; i < nums.length;i++){
add(nums[i]);
}
}
public int add(int val) {
if(queue.size() < k){
queue.offer(val);
}else{
if(queue.peek() < val){
// 踢出堆顶元素
queue.poll();
// 对堆顶赋值
queue.offer(val);
}
}
return queue.peek();
}
}
好了,到这里希望大家对优先队列这种数据结构以及它的应用的理解有所帮助,谢谢。