携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第15天,点击查看活动详情
优先队列
在之前我们已经学过栈和队列,栈是先进后出(FILO),队列是先进先出(FIFO)。今天要说的优先队列,首先它是队列的一种。入队列是正常入,不同的是,出队列按照优先级出。
实现机制
常用的实现实现形式有两种:
- 堆(Heap:Binary、Binomial,Fibonacci)
- 二叉搜索树(Binary Search Tree)
如下图一:大根堆,父节点的值要大于所有子节点的值;图二是小根堆,相反,父节点的值要小于它的所有子节点的值。
数据流中的第 K 大元素
描述
设计一个能找到数据流中第K大的树,实现KthLargest类:
- 实现KthLargest类初始化数据
- 实现add方法,不断给数据流中新增数据,动态获取数据流中第K大的数
分析
我们使用优先队列的思路来思考:目标是要得到数据流中第K大元素,而小根堆中的父节点的值是要都小于子节点的。如果K=3,而小根堆中是一个父节点,两个子节点,那父节点则是第3大的元素,不断加入新的值,再组成新的小跟堆,只需要返回父节点的值即可。思路很清晰,其实队列优先在很多语言中都有内置的类,如Java1.5就有了。在JavaScript中还没有,自己实现较复杂。我们这里就介绍下它的思路,面试中一般也很少要求来手动实现队列优先算法。
除了队列优先,还有更简单的方式。
核心就是不断对数组排序,每次新增一个元素,都需要排序,降序排序后,获取数据的第k-1个元素即可。
每次新增值都需要排序其实性能不是很好,我们可以加个判断:如果新加入的值不大于原第K大值,则不需要加入数据流再排序计算,因为小于第K值对结果就没有影响了。
程序实现
根据以上分析,程序实现如下:
var KthLargest = function(k, nums) {
this.nums = nums
this.getKMax = function () {
this.nums.sort((a, b) => {
return b - a
})
return this.nums[k-1]
}
this.kMax = this.getKMax()
};
/**
* @param {number} val
* @return {number}
*/
KthLargest.prototype.add = function(val) {
// 可能初始化nums是空
// 后续要加入的值如果不大于当前第k个大的值,则不需要加入数据流进行比较
if (!this.kMax || this.kMax < val) {
this.nums.push(val)
this.kMax = this.getKMax()
}
return this.kMax
};