數據結構 - 隊列

一 目錄

不折騰的前端,和鹹魚有什麼區別html

目錄
一 目錄
二 前言
三 初階:模擬實現隊列
四 初階:優先隊列
五 初階擊鼓傳花
六 進階:瀏覽器 Event Loop 機制
七 總結

二 前言

返回目錄前端

隊列,和棧有點相似,可是又不太同樣,隊列遵循 先進先出 的原則。node

假如將前面學過的棧用堆疊的書來比喻,須要一本一本拿,才能拿到最底部的書來講的話。git

那麼隊列就是排隊,假如你去銀行排隊,那麼,在前面的人先享受服務,完後前面的人先走。es6

形象點:github

入棧:(底部)A<-B<-C<-D(頂部)
出棧:(底部)A->B->C->D(頂部)

出隊列(頭部)A<-B<-C<-D 入隊列(尾部)
複製代碼

三 模擬實現隊列

返回目錄web

在瞭解了隊列後,咱們模擬實現一個隊列,加深咱們對隊列的印象。面試

首先,爲隊列聲明一些方法:算法

  • enqueue(element):向隊列尾部添加一個或者多個新的項。
  • dequeue():移除隊列的第一(即排在隊列最前面的)項,並返回被移除的元素。
  • front():返回隊列中第一個元素——最早被添加,也將是最早被移除的元素。隊列不作任何改動。
  • isEmpty():若是隊列中不包含任何元素,返回 true,不然返回 false
  • size():返回隊列中的元素個數,和數組的 length 屬性相似。

而後,咱們嘗試實現這些方法:segmentfault

實現代碼:

function Queue() {
  const items = [];
  // 1. 元素入隊列
  this.enqueue = function(element) {
    items.push(element);
  };
  // 2. 元素出隊列
  this.dequeue = function() {
    return items.shift();
  };
  // 3. 查看隊列頂部元素
  this.front = function() {
    return items[0];
  };
  // 4. 判斷隊列是否爲空
  this.isEmpty = function() {
    return items.length === 0;
  };
  // 5. 查看整個隊列長度
  this.size = function() {
    return items.length;
  };
  // 6. 查看整個隊列
  this.print = function() {
    console.log(items);
  }
};

let queue = new Queue();
queue.enqueue('1'); // [ '1' ]
queue.enqueue('2'); // [ '1', '2' ]
queue.dequeue(); // [ '2' ]
queue.dequeue(); // [ ]
queue.print(); // []
複製代碼

最後,若是純粹看 jsliang 寫的,沒圖沒視頻,小夥伴們很容易懵逼,這裏 jsliang 建議看各個大佬的文章或者經過下面章節的幾個案例進一步瞭解:

四 優先隊列

返回目錄

說到隊列,小夥伴們應該對一個詞很是有印象:

  • 插隊

這時候進行詞語聯想,就有了 黃牛黨強行插隊 等一系列 「惡」 詞。

可是,有時候 插隊 倒是很是有必要的,例如:

  1. 急診科候診室。先處理較急的病情,再處理次要點的。
  2. 登機順序。頭等艙和商務艙優於經濟艙,有些國家老人和孕婦(或者帶小孩的母親)優於其餘人。

那麼,優先隊列 如何實現呢?

function PriorityQueue() {
  // 1. 定義空數組
  const items = [];
  // 2. 定義隊列
  function QueueElement(element, priority) {
    this.element = element;
    this.priority = priority;
  }
  // 3. 實現入隊列方式
  this.enqueue = function(element, priority) {
    let queueElement = new QueueElement(element, priority);
    let added = false;
    for (let i = 0; i < items.length; i++) {
      // 若是能夠插隊,那麼就插入到隊列,且終止本次循環(減小時間浪費以及重複添加)
      if (queueElement.priority < items[i].priority) {
        items.splice(i, 0, queueElement);
        added = true;
        break;
      }
    }
    if (!added) {
      items.push(queueElement);
    }
  };
  // 4. 實現隊列打印
  this.print = function() {
    items.forEach((ele) => {
      console.log(`${ele.element} ${ele.priority}`);
    })
  };
  // 5. 其餘方法和默認的Queue實現相同
}

const priorityQueue = new PriorityQueue();
priorityQueue.enqueue('jsliang', 1);
priorityQueue.enqueue('JavaScriptLiang', 2);
priorityQueue.enqueue('梁峻榮', 1)
priorityQueue.print();
// jsliang 1
// 梁峻榮 1
// JavaScriptLiang 2
複製代碼

看完上面代碼,能夠感覺到 優先隊列 給人的感受是高大上的,它能夠幫助咱們進行 「更爲合理」 地排序,就比如假如你須要寫個程序,給急診用戶排隊,那麼咱們就能夠利用 優先隊列 的特色,讓患者獲得最快最準時的治療。

固然,更多的後續咱們在算法中進行講解。

五 擊鼓傳花

返回目錄

擊鼓傳花是一個遊戲。

在這個遊戲中,孩子們圍成一圈。

給某個孩子一朵花,而後他要儘快把這花傳遞給下一個孩子(順序傳遞)。

某一時刻傳花中止,手裏拿着花的孩子就被淘汰。

重複這個過程,直到剩下一個孩子。

實現方式以下:

/** * @name 隊列模擬 */
function Queue() {
  const items = [];
  // 1. 元素入隊列
  this.enqueue = function(element) {
    items.push(element);
  };
  // 2. 元素出隊列
  this.dequeue = function() {
    return items.shift();
  };
  // 3. 查看隊列頂部元素
  this.front = function() {
    return items[0];
  };
  // 4. 判斷隊列是否爲空
  this.isEmpty = function() {
    return items.length === 0;
  };
  // 5. 查看整個隊列長度
  this.size = function() {
    return items.length;
  };
  // 6. 查看整個隊列
  this.print = function() {
    console.log(items);
  }
};

/** * @name 擊鼓傳花 * @param {*} nameList 人名 * @param {*} num 淘汰的位置 */
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('淘汰了:' + eliminated);
  }
  return queue.dequeue();
}

const names = ['name1', 'name2', 'name3', 'name4', 'name5'];
const winner = hotPotato(names, 7);
console.log('贏家是:' + winner);
// 淘汰了:name3
// 淘汰了:name2
// 淘汰了:name5
// 淘汰了:name4
// 贏家是:name1
複製代碼

在此次遊戲中,淘汰順序是:

  1. name3
  2. name2
  3. name5
  4. name4

最後剩下 name1,退出循環,咱們將其推出棧(此時棧爲空)。

固然,這裏咱們固定了傳遞進來的數字爲 7,若是咱們經過隨機來指定一個,那麼應該會更加有趣點:

隨機式擊鼓傳花

// ...主體代碼如上

const names = ['name1', 'name2', 'name3', 'name4', 'name5'];
const winner = hotPotato(names, Math.floor(Math.random() * 10 + 1));
console.log('贏家是:' + winner);
// 淘汰了:name1
// 淘汰了:name4
// 淘汰了:name2
// 淘汰了:name3
// 贏家是:name5
複製代碼

如今淘汰的位置不固定了,是否是以爲比起原版的有點味道了~

六 進階:瀏覽器 Event Loop

返回目錄

說到隊列,若是單純講基礎點,相信不少小夥伴都不會買單,因此我們能夠進一步探索:

  • 瀏覽器 Event Loop 機制

注:這也是面試常備的一道題

首先,爲何須要 Event Loop?

由於 JavaScript 是單線程的。

單線程意味着,全部任務都須要排隊,前一個任務結束,纔會執行後一個任務。

若是前一個任務耗時很長,那麼後一個任務就不得不一直等着。

爲了協調事件(event),用戶交互(user interaction),腳本(script),渲染(rendering),網絡(networking)等,用戶代理(user agent)必須使用事件循環(event loops)。

而後,瞭解完 Event Loop 的基礎內容,我們經過文章進一步探索 Event Loop。

爲此 jsliang 特意翻閱了十幾篇文章,從 Event Loop 的機制講起,經過嘗試描述瀏覽器的 Event Loop,再進一步講解 Node.js 的 Event Loop,來幫助本身和小夥伴們深刻探索,仔細瞭解這一塊內容:

最後,看到這裏,相信小夥伴們對此有個簡單瞭解,對隊列這個詞也有了進一步的深刻了解。

參考文獻:

  1. 《Tasks, microtasks, queues and schedules》 - Jake
  2. 《完全搞懂瀏覽器 Event-loop》 - 劉小夕
  3. 《完全理解 JS Event Loop(瀏覽器環境)》 - 93
  4. 《完全弄懂瀏覽器端的 Event-Loop》 - 長可
  5. 《什麼是瀏覽器的事件循環(Event Loop)?》 - 魚子醬
  6. 《理解event loop(瀏覽器環境與nodejs環境)》 - sugerpocket
  7. 《從 event loop 規範探究 JavaScript 異步及瀏覽器更新渲染時機》 - 楊敬卓
  8. 《跟着 Event loop 規範理解瀏覽器中的異步機制》 - fi3ework
  9. 《不要混淆 nodejs 和瀏覽器中的 event loop》 - youth7
  10. 《瀏覽器的 event loop 和 node 的 event loop》 - 金大光
  11. 《瀏覽器與 Node 的事件循環(Event Loop)有何區別?》 - 浪裏行舟
  12. 《瀏覽器和 Node 不一樣的事件循環(Event Loop)》 - toBeTheLight
  13. 《let 和 const 命令》 - 阮一峯
  14. 《Node.js Event Loop》 - Node.js 官網

七 總結

返回目錄

這樣,咱們就 暫時 完成了隊列的基礎瞭解學習,由於你站在甲板上,你是看不到整艘郵輪是怎麼運行的,因此我們會慢慢探索,經過各類 LeetCode 題專門訓練,逐步豐富咱們的視野。

在探索這些內容的過程當中,jsliang 也對某些內容感到困惑,基本上,碰到一些新內容,會先抄一遍(照着敲一份),細細理解它的意思,而後根據自身理解,想一想有沒有其餘實現方式(例如經過定時器來實現擊鼓傳花)。

我的以爲,人的一輩子都在學習,關鍵是你能不能持續探索下去,以及你的探索方式對你的啓發。

僅此而已。


不折騰的前端,和鹹魚有什麼區別!

jsliang 會天天更新一道 LeetCode 題解,從而幫助小夥伴們夯實原生 JS 基礎,瞭解與學習算法與數據結構。

浪子神劍 會天天更新面試題,以面試題爲驅動來帶動你們學習,堅持天天學習與思考,天天進步一點!

掃描上方二維碼,關注 jsliang 的公衆號(左)和 浪子神劍 的公衆號(右),讓咱們一塊兒折騰!

知識共享許可協議
jsliang 的文檔庫梁峻榮 採用 知識共享 署名-非商業性使用-相同方式共享 4.0 國際 許可協議進行許可。
基於github.com/LiangJunron…上的做品創做。
本許可協議受權以外的使用權限能夠從 creativecommons.org/licenses/by… 處得到。

相關文章
相關標籤/搜索