不折騰的前端,和鹹魚有什麼區別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 建議看各個大佬的文章或者經過下面章節的幾個案例進一步瞭解:
說到隊列,小夥伴們應該對一個詞很是有印象:
這時候進行詞語聯想,就有了 黃牛黨,強行插隊 等一系列 「惡」 詞。
可是,有時候 插隊 倒是很是有必要的,例如:
那麼,優先隊列 如何實現呢?
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
複製代碼
在此次遊戲中,淘汰順序是:
最後剩下 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?
由於 JavaScript 是單線程的。
單線程意味着,全部任務都須要排隊,前一個任務結束,纔會執行後一個任務。
若是前一個任務耗時很長,那麼後一個任務就不得不一直等着。
爲了協調事件(event),用戶交互(user interaction),腳本(script),渲染(rendering),網絡(networking)等,用戶代理(user agent)必須使用事件循環(event loops)。
而後,瞭解完 Event Loop 的基礎內容,我們經過文章進一步探索 Event Loop。
爲此 jsliang 特意翻閱了十幾篇文章,從 Event Loop 的機制講起,經過嘗試描述瀏覽器的 Event Loop,再進一步講解 Node.js 的 Event Loop,來幫助本身和小夥伴們深刻探索,仔細瞭解這一塊內容:
最後,看到這裏,相信小夥伴們對此有個簡單瞭解,對隊列這個詞也有了進一步的深刻了解。
參考文獻:
這樣,咱們就 暫時 完成了隊列的基礎瞭解學習,由於你站在甲板上,你是看不到整艘郵輪是怎麼運行的,因此我們會慢慢探索,經過各類 LeetCode 題專門訓練,逐步豐富咱們的視野。
在探索這些內容的過程當中,jsliang 也對某些內容感到困惑,基本上,碰到一些新內容,會先抄一遍(照着敲一份),細細理解它的意思,而後根據自身理解,想一想有沒有其餘實現方式(例如經過定時器來實現擊鼓傳花)。
我的以爲,人的一輩子都在學習,關鍵是你能不能持續探索下去,以及你的探索方式對你的啓發。
僅此而已。
不折騰的前端,和鹹魚有什麼區別!
jsliang 會天天更新一道 LeetCode 題解,從而幫助小夥伴們夯實原生 JS 基礎,瞭解與學習算法與數據結構。
浪子神劍 會天天更新面試題,以面試題爲驅動來帶動你們學習,堅持天天學習與思考,天天進步一點!
掃描上方二維碼,關注 jsliang 的公衆號(左)和 浪子神劍 的公衆號(右),讓咱們一塊兒折騰!
jsliang 的文檔庫 由 梁峻榮 採用 知識共享 署名-非商業性使用-相同方式共享 4.0 國際 許可協議進行許可。
基於github.com/LiangJunron…上的做品創做。
本許可協議受權以外的使用權限能夠從 creativecommons.org/licenses/by… 處得到。