当前位置: 首页>后端>正文

javascript 之优先队列

大家好,我是 17

优先队列中的每个元素都有各自的优先级,优先级最高的元素最先得到服务;优先级相同的元素按照其在优先队列中的顺序得到服务。

优先队列至少需要支持下述操作:

  • 插入带优先级的元素(insert_with_priority)
  • 取出具有最高优先级的元素(pull_highest_priority_element)
  • 查看最高优先级的元素(peek):O(1) 时间复杂度

其它语言一般都有相应的函数,可惜 js 没有,所以补上一个。

class PriorityQueue {
  constructor(compare) {
  
     if(typeof compare !=='function'){
        throw new Error('compare function required!')
     }
  
     this.data = []
     this.compare = compare
    
  }
  //二分查找 寻找插入位置
  search(target) {
    let low = 0, high = this.data.length
    while (low < high) {
      let mid = low + ((high - low) >> 1)
      if (this.compare(this.data[mid], target) > 0) {
        high = mid
      }
      else {
        low = mid + 1
      }
    }
    return low;
  }
  //添加
  push(elem) {
    let index = this.search(elem)
    this.data.splice(index, 0, elem)
    return this.data.length
  }
  //取出最优元素
  pop() {
    return this.data.pop()
  }
  //查看最优元素
  peek() {
    return this.data[this.data.length - 1];
  }
}

先不要看代码实现,把 PriorityQueue 当作黑盒看看怎么使用。

最大值最小值优先

每次取出的是整个队列中的最小值。为了达到这个目的,队列是有序的,且升序排列。

拿实际值演练一下。

let qeen = new PriorityQueue((a, b) => a-b)
queen.push(3);
console.log(queen.data)  //[3]
queen.push(1);
console.log(queen.data)  //[1,3]
qeeun.push(2);
console.log(queen.data)  //[1,2,3]

每次播入新元素,都会自动把元素插入到正确的位置,保持队列按升序排列,关键就在于 (a,b) => a-b 这个 compare 方法 是这经告诉 PriorityQueue 如何插入元素。这个compare 方法和 Array的 sort 方法用的 compare 是一样的,是为了好记,只要会sort 方法,就会用 PriorityQueue

let arr = [1, 3, 2]
//按升序
arr.sort((a, b) => a - b) //  [1,2,3]

//优先队列的数据按升序排列
let qeen = new PriorityQueue((a, b) => a - b)
queen.push(3);
queen.push(2);
queen.push(1);

consolelog(queen.data) //[ 1,2,3 ]

//按降序
arr.sort((a, b) => b - a) //  [3,2,1]
//优先队列的数据按降序排列
let qeen = new PriorityQueue((a, b) => b - a) 

PriorityQueue 是特意采用数组已有的方法名,就是为了好记。

除了 push pop peek,还有 search 方法也是公开的,而且查找速度也是很快的。如果不知道目标元素的 index,只知道值,可以用 search 查找。

注意 search 找到的是播入位置

复杂度

时间复杂度

  • pop O(1)
  • peek O(1)
  • push O(n),
  • search O(logn)

有的同学这样写的,没有用 splice 方法

//从小到大排列,播入元素,向前冒泡
function insert(n) {
  this.data.push(n);
  let len = this.data.len
  while (len-- > 0) {
    if (this.data[len] < this.data[len - 1]) {
      //交换
      [this.data[len], this.data[len - 1]] = [this.data[len - 1], this.data[len]]
    }
    else {
      break
    }
  }
}

理由是 既然 splice 方法最后也是 O(n),我这样也是 O(n)。但实际上 splice 方法在速度比这样循环要快多了。

虽然在插入的时候较慢,但取出数据的时候是 O(1),取 前 N 个数据的时间也比树快。

如果要优化插入的时间,可以用红黑树,但取出数据后还得维护树, 插入,取(删除)都是 O(logn)。而且用树的话,就不能通过 index 用 O(1) 的时间来找到元素了。

所以当前版本的 PriorityQueue 还是可以优先考虑的。

空间复杂度

data存的值不算,额外的空间为 O(1)

最后提醒一下,PriorityQueue 不是光能存数字,是可以存对象的。只要给出 比较对象的方法即可,和数组的 sort 方法一样。

参考

  • 简单易懂的红黑树原理及实现(js)

https://www.xamrdz.com/backend/3q41928980.html

相关文章: