【JavaScript數據結構系列】04-優先隊列PriorityQueue


【JavaScript數據結構系列】04-優先隊列PriorityQueue

碼路工人 CoderMonkey
轉載請注明作者與出處


## 1. 認識優先級隊列

經典的案例場景:

  • 登機時經濟艙的普通隊列與頭等艙的優先級隊列
  • 股票交易時基於時間和價格的成交規則上,量大優先的優先級隊列

再用我們打飯的例子:
假定規則:飢餓等級0級最高,需要馬上進食
下圖同學C優先級高於同學B,插隊在同學A后面
PriorityQueue.png

2. 代碼實現

注:
ES6 版的代碼實現請查看 npm 包 data-struct-js 代碼
Github/Gitee 上都能找到

npm install data-struct-js


在隊列 Queue 的基礎上,我們來實現一下優先級隊列。

  • 優先級隊列只在入隊操作上與普通隊列不同,其它方法相同
  • 參考上一節里的基於棧實現的隊列,也稍稍修改下隊列實現的代碼

入隊操作實現分析:

  • 在創建元素時,我們需要一個優先級參數/字段(priority)
  • 並對優先級做檢查,以及默認值設置
  • 空隊列時直接入隊
  • 非空時,從隊列頭部開始比對每一個元素,新元素優先級高時插入
  • 比對到最后一個也沒能插入時(新元素優先級最低)添加到隊尾

主要是 enqueue 方法和 QueueElement 的封裝。

// 優先隊列
function PriorityQueue() {
  this.__items = []

  /**
   *隊列元素對象
   *優先級默認為最低
   */
  function QueueElement(element, priority) {
    // check priority
    if(typeof(priority) != 'number' || Number.isNaN(priority)) {
      // min-level: Infinity
      priority = Infinity
    }
    this.__element = element
    // max-level: 0
    this.__priority = priority

    QueueElement.prototype.priority = function() {
      return this.__priority
    }

    QueueElement.prototype.toString = function() {
      return this.__element.toString.apply(this.__element)
    }
  }

  // 入隊方法
  PriorityQueue.prototype.enqueue = function(element, priority) {
    var queueElement = new QueueElement(element, priority)

    // 空隊列時直接入隊
    if(this.__items.length === 0) {
      this.__items.push(queueElement)
    }
    // 非空隊列入隊需比較優先級
    else {
      var added = false
      for(var i=0;i<this.__items.length;i++) {
        if(queueElement.priority() < this.__items[i].priority()) {
          this.__items.splice(i, 0, queueElement)
          added = true
          break
        }
      }
      if(!added) {
        this.__items.push(queueElement)
      }
    }
  }
}

自己封裝的優先級隊列中優先級priority也可以作為復雜對象上的一個屬性,無需另傳參數

完整代碼(用到的上一節中的 deepCopy 方法也一並貼上吧)

PriorityQueue.js
這里為了方便查看代碼寫全了,
實際上重復的部分可以繼承普通隊列

// 優先隊列
function PriorityQueue() {
  this.__items = []

  /**
   *隊列元素對象
   *優先級默認為最低
   */
  function QueueElement(element, priority) {
    // check priority
    if(typeof(priority) != 'number' || Number.isNaN(priority)) {
      // min-level: Infinity
      priority = Infinity
    }
    this.__element = element
    // max-level: 0
    this.__priority = priority

    QueueElement.prototype.priority = function() {
      return this.__priority
    }

    QueueElement.prototype.toString = function() {
      return this.__element.toString.apply(this.__element)
    }
  }

  // 入隊方法
  PriorityQueue.prototype.enqueue = function(element, priority) {
    var queueElement = new QueueElement(element, priority)

    // 空隊列時直接入隊
    if(this.__items.length === 0) {
      this.__items.push(queueElement)
    }
    // 非空隊列入隊需比較優先級
    else {
      var added = false
      for(var i=0;i<this.__items.length;i++) {
        if(queueElement.priority() < this.__items[i].priority()) {
          this.__items.splice(i, 0, queueElement)
          added = true
          break
        }
      }
      if(!added) {
        this.__items.push(queueElement)
      }
    }
  }

  PriorityQueue.prototype.dequeue = function() {
    return this.getItems().shift()
  }

  PriorityQueue.prototype.front = function () {
    return this.__items.length === 0 ? undefined : this.getItems()[0]
  }

  PriorityQueue.prototype.getItems = function() {
    return deepCopy(this.__items)
  }

  PriorityQueue.prototype.isEmpty = function () {
    return this.__items.length === 0
  }

  PriorityQueue.prototype.size = function () {
    return this.__items.length
  }

  PriorityQueue.prototype.clear = function () {
    this.__items.length = 0
  }

  PriorityQueue.prototype.toString = function () {
    var arrStr = this.__items.map((qe)=>{
      return qe.toString()
    })
    return arrStr.join('\r\n')
  }
}
function deepCopy(source) {
  var dest
  if(Array.isArray(source)) {
    dest = []
    for (let i = 0; i < source.length; i++) {
      dest[i] =deepCopy(source[i])
    }
  }
  else if(toString.call(source) === '[object Object]') {
    dest = {}
    for(var p in source){
      if(source.hasOwnProperty(p)){
        dest[p]=deepCopy(source[p])
      }
    }
  }
  else {
    dest = source
  }
  return dest
}

測試一下

var pq = new PriorityQueue()

pq.enqueue({name: 'A-First Element | Priority:1', age: 18, toString: function(){return this.name}}, 1)
pq.enqueue({name: 'B-Second Element | Priority:3', age: 18, toString: function(){return this.name}}, 3)
pq.enqueue({name: 'C-Third Element | Priority:2', age: 18, toString: function(){return this.name}}, 2)

console.log(pq.toString())

以優先級分別為 1 -> 3 -> 2 的順序添加元素,
輸出結果為:

A-First Element | Priority:1
C-Third Element | Priority:2
B-Second Element | Priority:3

收工。


做了一份 npm 工具包 data-struct-js
基於 ES6 實現的 JavaScript 數據結構,
雖然這個小輪子很少會被使用,
也許對於初學者學習 JavaScript 會有點幫助。
只要簡單 install 一下即可,感興趣的話還可以去
GitHub / Gitee 看源碼。(來 Star 一個吧)

npm install data-struct-js --save-dev

https://github.com/CoderMonkie/data-struct-js
https://gitee.com/coder-monkey/data-struct-js

最后,感謝您的閱讀和支持~


-end-


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM