佇列-單端佇列

致于数据科学家的小陈發表於2024-04-08

佇列和棧非常類似, 棧的一端是封閉的, 類似一口深井, 遵循先進後出原則 FILO.

佇列則兩端是放開的, 抽象於現實世界的排隊現象, 遵循先進先出原則 FIFO.

佇列在尾部進行元素的新增, 稱為 "入隊", 然後從頭部移除元素, 成為 "出隊". 生活中我們去坐火車進站檢票, 去某個機關辦理業務, 去用印表機印一疊檔案等都是需要 "排隊", 先來的先處理, 後來的往後站, 不允許插隊哦.

建立佇列

最簡單方式可直接用 js 陣列實現, 隊尾用 arr.push(), arr.pop() , 隊首用 arr.unshift(), arr.shift()

但從元素獲取層面希望能更高效, 還是覺得用 js物件 來實現更合適一些呢.

class Queue {
  constructor() {
    this.count = 0  // 控制佇列大小, 非長度
    this.fontIndex = 0  // 隊首第一個元素索引
    this.obj = {}
  }
}

然後是要給佇列宣告一些常用方法

  • enqueue () 向隊尾新增元素
  • dequeue () 向隊首移除元素, 並返回
  • peek () 返回隊首元素
  • isEmpty () 檢查是否為空佇列, 返回 true 或者 false
  • size () 返回佇列的元素個數, 即佇列長度, 類似陣列的 length

元素入隊

即元素必須要從隊尾進行新增, 因為用的底層是 JS物件, 即用 count 作為鍵, 新增後自增 1 即可.

class Queue {
  constructor() {
    this.count = 0 
    this.frontIndex = 0  
    this.obj = {}
  }
  // 元素入隊
  enqueue(item) {
    this.obj[this.count] = item 
    this.count += 1
  }
}

佇列長度

即佇列中元素個數, 即用佇列的長度 減去 隊首元素的索引即可, 當這個值為0 , 則說明是空佇列了.

class Queue {
  constructor() {
    this.count = 0 
    this.frontIndex = 0  
    this.obj = {}
  }
  // 佇列長度
  size() {
    return this.count - this.frontIndex
  }
  // 佇列是否為空
  isEmpty() {
    this.size() == 0
  }
}

還是來簡單模擬一下,直觀理解一波這個長度的計算.

假設左邊是隊尾, 右邊是隊首, 而且是有序的

先入隊a: { 0:a },
再入隊b: { 1:b,  0:a },
再入隊c: { 2:c, 1:b,  0:a }

此時的     this.count = 3;  
隊首索引是: this.frontIndex = 0

根據先進先出, 對 a 進行出隊

此時到對首元素是0, 動態表示為 obj[this.frontIndex], 值是 a
然後進行刪除後, 此時的佇列是 { 2:c, 1:b }, 即當前隊首元素索引 從 0 變到 1
動態表示即 this.frontIndex + 1 表示此時隊首的元素

元素出隊

只要弄清楚瞭如何計算佇列長度, 非空下的 frontIndex 就是隊首長度, 刪掉它即可.

class Queue {
  constructor() {
    this.count = 0 
    this.frontIndex = 0  
    this.obj = {}
  }
  // 佇列是否為空
  isEmpty() {
    this.count - this.frontIndex == 0
  }
  // 元素出隊
  dequeue() {
    if (this.isEmpty()) return undefined 

    let frontItem = this.obj[this.frontIndex]
    delete this.obj[this.frontIndex]

    this.frontIndex += 1 // 更新隊首元素索引
    return frontItem
  }
}

清空佇列元素

粗暴方式是直接指向空, 或者一直進行 dequeue() 直到返回 undefined 為止.

  // 清空佇列
  clear() {
    this.obj = {}
    this.frontIndex = 0
    this.count = 0
  }
}

檢視隊首元素和全佇列

隊首就是索引為 this.frontIndex 的值, 檢視全部可以類似棧封裝一個 toString 方法

class Queue {
  constructor() {
    this.count = 0 
    this.frontIndex = 0  
    this.obj = {}
  }
  // 檢視隊首元素
  peek() {
    if (this.isEmpty()) return undefined
    return this.obj[this.frontIndex]
  }
  // 檢視全佇列
  toString() {
    if (this.isEmpty()) return undefined

    let objString = `${this.obj[this.frontIndex]}`
    for (let i = this.frontIndex + 1; i < this.count; i++) {
      objString = `${objString}, ${this.obj[i]}`
    }
    return objString
  }
}

這樣以來,我們的佇列就基本建好了, 然後我們來整體測試一波

class Queue {
  constructor() {
    this.count = 0 
    this.frontIndex = 0  
    this.obj = {}
  }
  // 元素入隊
  enqueue(item) {
    this.obj[this.count] = item 
    this.count += 1
  }
  // 佇列長度
  size() {
    return this.count - this.frontIndex
  }
  // 佇列是否為空
  isEmpty() {
    this.count - this.frontIndex == 0
  }
  // 元素出隊
  dequeue() {
    if (this.isEmpty()) return undefined 

    let frontItem = this.obj[this.frontIndex]
    delete this.obj[this.frontIndex]

    this.frontIndex += 1 // 更新隊首元素索引
    return frontItem
  }
  // 清空佇列
  clear() {
    this.obj = {}
    this.frontIndex = 0
    this.count = 0
  }
  // 檢視隊首元素
  peek() {
    if (this.isEmpty()) return undefined
    return this.obj[this.frontIndex]
  }
  // 檢視全佇列
  toString() {
    if (this.isEmpty()) return undefined

    let objString = `${this.obj[this.frontIndex]}`
    for (let i = this.frontIndex + 1; i < this.count; i++) {
      objString = `${objString}, ${this.obj[i]}`
    }
    return objString
  }
}


// test 
const queue = new Queue()

queue.enqueue('youge')
queue.enqueue('yaya')
queue.enqueue('jack')

// 檢驗入隊
console.log('此時的佇列是: ', queue.toString());
console.log('佇列長度是: ', queue.size());
console.log('隊首元素是: ', queue.peek());

// 檢驗出隊
console.log('出隊的元素是: ', queue.dequeue());

console.log('此時的佇列是: ', queue.toString());
console.log('佇列長度是: ', queue.size());
console.log('隊首元素是: ', queue.peek());

// 剩下元素出隊
console.log('出隊的元素是: ', queue.dequeue());
console.log('出隊的元素是: ', queue.dequeue());

// 空了
console.log('此時的佇列是: ', queue.toString());
console.log('佇列長度是: ', queue.size());
console.log('隊首元素是: ', queue.peek());

檢視一下結果:

PS F:\algorithms> node queue.js

此時的佇列是:  youge, yaya, jack
佇列長度是:  3
隊首元素是:  youge

出隊的元素是:  youge
此時的佇列是:  yaya, jack
佇列長度是:  2
隊首元素是:  yaya

出隊的元素是:  yaya
出隊的元素是:  jack

此時的佇列是:  undefined
佇列長度是:  0
隊首元素是:  undefined

這樣就實現了一個單端的佇列, 後面接著會再來實現一個雙端的佇列哦.

相關文章