优先队列

130 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第15天,点击查看活动详情

优先队列

在之前我们已经学过栈和队列,栈是先进后出(FILO),队列是先进先出(FIFO)。今天要说的优先队列,首先它是队列的一种。入队列是正常入,不同的是,出队列按照优先级出。

实现机制

常用的实现实现形式有两种:

  1. 堆(Heap:Binary、Binomial,Fibonacci)
  2. 二叉搜索树(Binary Search Tree)

如下图一:大根堆,父节点的值要大于所有子节点的值;图二是小根堆,相反,父节点的值要小于它的所有子节点的值。

image.png

数据流中的第 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
};