數據結構學習之隊列

一、什麼是隊列?

隊列和棧有着明顯的區別,隊列是一種特殊的線性表有着先進先出的特色。它只容許在表頭進行刪除操做,在表尾進行添加操做。javascript

入隊列示意圖 java

出隊列示意圖 面試

隊列有許多的應用,好比javascript的事件循環機制,就是經過事件隊列來存儲異步操做的回調函數。數組

好比逐層打印一顆樹上的節點。像kafka,rabbitmq這類消息隊列,其形式就是一種隊列,消息生產者把消息放入隊列中(尾部),消費者從隊列裏取出消息進行處理(頭部),只不過背後的實現更爲複雜。數據結構

若是你瞭解一點socket,那麼你應該知道當大量客戶端向服務端發起鏈接,而服務端忙不過來的時候,就會把這些請求放入到隊列中,先來的先處理,後來的後處理,隊列滿時,新來的請求直接拋棄掉。異步

數據結構在系統設計中的應用很是普遍,只是咱們水平達不到那個級別,知道的太少,但若是能理解並掌握這些數據結構,那麼就有機會在工做中使用它們並解決一些具體的問題,當咱們手裏除了錘子還有電鋸時,那麼咱們的眼裏就不僅是釘子,解決問題的思路也會更加開闊socket

二、隊列的實現

首先先定義一些經常使用的方法:函數

  • enqueue 從隊列尾部添加一個元素
  • dequeue 從隊列頭部刪除一個元素
  • head 返回頭部的元素,注意,不是刪除
  • size 返回隊列大小
  • clear 清空隊列
  • isEmpty 判斷隊列是否爲空
  • tail 返回隊列尾節點

而後咱們逐一實現學習

let Queue = (function () {
  let items = new WeakMap() 
  // WeakMap結構與Map結構基本相似。區別是它只接受對象做爲鍵名,
  // 不接受其餘類型的值做爲鍵名。鍵名是對象的弱引用,當對象被回收後,
  // WeakMap自動移除對應的鍵值對,WeakMap結構有助於防止內存泄漏。 
  class Queue {
    constructor() {
      items.set(this, [])
    }
    // 入隊列
    enqueue(item) {
      let queue = items.get(this)
      queue.push(item)
    }
    // 出隊列
    dequeue() {
      return items.get(this).shift()
    }
    // 返回隊列頭
    head() {
      let queue = items.get(this)
      return queue[0]
    }
    // 返回隊列大小
    size() {
      let queue = items.get(this)
      return queue.length
    }
    // 清空隊列
    clear() {
      items.set(this, [])
    }
    // 判斷隊列是否爲空
    isEmpty() {
      let queue = items.get(this)
      return queue.length === 0
    }
    // 返回隊尾
    tail() {
      let queue = items.get(this)
      return queue[queue.length - 1]
    }
  }
  return Queue
})
複製代碼

三、隊列常見的應用

3.一、約瑟夫環

有一個數組a[100]存放0--99;要求每隔兩個數刪掉一個數,到末尾時循環至開頭繼續進行,求最後一個被刪掉的數。好比:前10個數是 0 1 2 3 4 5 6 7 8 9 10,所謂每隔兩個數刪掉一個數,其實就是把 2 5 8 刪除掉。ui

思路分析

  • 從隊列頭部刪除一個元素,index+1
  • 若是index%3 == 0,就說明這個元素是須要刪除的元素,若是不等於0,就不是須要被刪除的元素,則把它添加到隊列的尾部

代碼實現

// 先初始化一個數據
var arr_list = [];
for(var i=0;i< 100;i++){
    arr_list.push(i);
}
// 定義功能函數
function del(arr) {
    let queue = new Queue()
    // 將全部元素入隊列
    for(var i=0;i< arr_list.length;i++){
        queue.enqueue(arr_list[i]);
    }
    let index = 0
    while(queue.size() != 1) {
        index ++
        index%3 === 0 ?  queue.enqueue(queue.dequeue()) : queue.dequeue()
    }
    return queue.head() // 返回隊列中惟一的元素
}
// 調用
del(arr_list)
複製代碼

3.二、用隊列來實現一個棧

思路分析

  • push, 實現push方法時,若是兩個隊列都爲空,那麼默認向queue_1裏添加數據,若是有一個不爲空,則向這個不爲空的隊列裏添加數據

  • top,兩個隊列,或者都爲空,或者有一個不爲空,只須要返回不爲空的隊列的尾部元素便可

  • pop,pop方法是比較複雜,pop方法要刪除的是棧頂,但這個棧頂元素實際上是隊列的尾部元素。每一次作pop操做時,將不爲空的隊列裏的元素一次刪除並放入到另外一個隊列中直到遇到隊列中只剩下一個元素,刪除這個元素,其他的元素都跑到以前爲空的隊列中了。

代碼實現

function queueToStack() {
  let queue_1 = new Queue()
  let queue_2 = new Queue()
  let data_queue = null
  let empty_queue = null
  // 確認每一個隊列的用途
  let initQueue = () => {
    if (queue_1.isEmpty() && queue_2.isEmpty()) {
      data_queue = queue_1
      empty_queue = queue_2
    } else if (queue_1.isEmpty()) {
      data_queue = queue_2
      empty_queue = queue_1
    } else {
      data_queue = queue_1
      empty_queue = queue_2
    }
  }
  this.push = (item) => {
    initQueue()
    data_queue.enqueue(item)
  }
  this.top = () => {
    initQueue()
    return data_queue.tail()
  }
  this.pop = () => {
    initQueue()
    while (data_queue.size() > 1) {
      empty_queue.enqueue(data_queue.dequeue())
    }
    return data_queue.dequeue()
  }
}
複製代碼

隊列還有其餘不少在面試中可能會問道的面試題好比:打印楊輝三角以及迷宮問題,這些用隊列來實現可能會更加的方便。

四、優先隊列

function PriorityQueue() {
    let items = []
    function QueueElement(element, priority) {
        this.element = element
        this.priority = priority // 設置優先級別
    }
    this.enqueue = function(element, priority) {
        let queueElement = new QueueElement(element, priority)
        let add = false
        for(let i = 0; i < items.length; i++) { // 遍歷隊列找到優先級比它小的元素
            if(queueElement.priority > items[i].priority) {
                items.splice(i, 0, queueElement) // 而後添加到該隊列
                add = true
                break
            }
        }
        if(!add) { // 若是沒有找到直接添加到隊列末尾
            items.push(queueElement)
        }
    }
}
複製代碼

五、循環隊列

function hotPotato(nameList, num) {
    let queue = new Queue()
    for(let i = 0; i < nameList.length; i++) {
        queue.enqueue(nameList[i])
    }
    let eliminated = ''
    while(queue.size() > 1) {
        for(let i = 0; i < num; i++) {
            queue.enqueue(queue.dequeue)
        }
        eliminated = queue.dequeue()
        console.log(eliminates + 'out')
    }
    return queue.dequeue()
}
複製代碼

六、總結

在學習了隊列以後與棧對比進一步瞭解了各個數據結構的用法,以及使用的方便之處。固然也爲面試打下了基礎。

相關文章
相關標籤/搜索