一、定義
前面我們學習了棧的實現,佇列和棧非常類似,但是使用了不同的原則,而非後進先出。
佇列是遵循FIFO(First In First Out,先進先出)原則的一組有序的項。佇列在尾部新增新元素,並從頂部移除元素。最新新增的元素必須排在佇列的末尾。
在電腦科學中,一個最常見的例子就是列印佇列。比如說我們要列印五份文件。我們會開啟每個文件,然後點選列印按鈕。每個文件都會被髮送至列印佇列。第一個傳送到列印佇列的文件會首先被列印,以此類推,直到列印完所有文件。
二、佇列的實現
2.1 普通佇列
建立普通佇列類:
// Queue類
function Queue () {
this.items = [];
this.enqueue = enqueue;
this.dequeue = dequeue;
this.front = front;
this.isEmpty = isEmpty;
this.size = size;
this.clear = clear;
this.print = print;
}
複製程式碼
佇列裡面有一些宣告的輔助方法:
- enqueue(element):向佇列尾部新增新項
- dequeue():移除佇列的第一項(即排在佇列最前面的項),並返回被移除的元素
- front():返回佇列中第一個元素,佇列不做任何變動,和Stack的peek()方法類似
- isEmpty():如果佇列中不包含任何元素,返回true,否則返回false
- size():返回佇列包含的元素個數,與陣列的length屬性類似
- print():列印佇列中的元素
- clear():清空整個佇列
下面我們來一一實現這些輔助方法:
// 向佇列尾部新增元素
function enqueue (element) {
this.items.push(element);
}
// 移除佇列的第一個元素,並返回被移除的元素
function dequeue () {
return this.items.shift();
}
// 返回佇列的第一個元素
function front () {
return this.items[0];
}
// 判斷是否為空佇列
function isEmpty () {
return this.items.length === 0;
}
// 獲取佇列的長度
function size () {
return this.items.length;
}
// 清空佇列
function clear () {
this.items = [];
}
// 列印佇列裡的元素
function print () {
console.log(this.items.toString());
}
複製程式碼
建立普通佇列例項進行測試:
// 建立Queue例項
var queue = new Queue();
console.log(queue.isEmpty()); // true
queue.enqueue("John"); // undefined
queue.enqueue("Jack"); // undefined
queue.enqueue("Camila"); // undefined
queue.print(); // "John,Jack,Camila"
console.log(queue.size()); // 3
console.log(queue.isEmpty()); // false
queue.dequeue(); // "John"
queue.dequeue(); // "Jack"
queue.print(); // "Camila"
queue.clear(); // undefined
console.log(queue.size()); // 0
複製程式碼
2.2 優先佇列
2.2.1 定義
普通佇列的新增和移除只依賴於先後順序,先來的先新增,後來的後新增,然後按照先後順序依次從佇列移除。
但是,還有一種佇列叫優先佇列,元素的新增和移除是依賴優先順序的。
一個現實的例子就是機場登機的順序。頭等艙和商務艙乘客的優先順序要高於經濟艙乘客。再比如火車,老年人、孕婦和帶小孩的乘客是享有優先檢票權的。
2.2.2 分類
優先佇列分為兩類:
- 最小優先佇列
- 最大優先佇列
最小優先佇列是把優先順序的值最小的元素被放置到佇列的最前面(代表最高的優先順序)。比如有四個元素:"John", "Jack", "Camila", "Tom",他們的優先順序值分別為4,3,2,1。
那麼最小優先佇列排序應該為:"Tom","Camila","Jack","John"。
最大優先佇列正好相反,把優先順序值最大的元素放置在佇列的最前面,以上面的為例,最大優先佇列排序應該為:"John", "Jack", "Camila", "Tom"。
2.2.2 實現
實現一個優先佇列,有兩種選項:
- 設定優先順序,根據優先順序正確新增元素,然後和普通佇列一樣正常移除
- 設定優先順序,和普通佇列一樣正常按順序新增,然後根據優先順序移除
這裡最小優先佇列和最大優先佇列我都採用第一種方式實現,大家可以嘗試一下第二種。
所以我只重寫enqueue()方法和print()方法,其他方法和上面的普通佇列完全相同。完整程式碼見我的github。
實現最小優先佇列:
// 定義最小優先佇列
function MinPriorityQueue () {
this.items = [];
this.enqueue = enqueue;
this.dequeue = dequeue;
this.front = front;
this.isEmpty = isEmpty;
this.size = size;
this.clear = clear;
this.print = print;
}
複製程式碼
實現最小優先佇列enqueue()方法和print()方法:
// 優先佇列新增元素,要根據優先順序判斷在佇列中的插入順序
function enqueue (element, priority) {
var queueElement = {
element: element,
priority: priority
};
if (this.isEmpty()) {
this.items.push(queueElement);
} else {
var added = false;
for (var i = 0; i < this.size(); i++) {
if (queueElement.priority < this.items[i].priority) {
this.items.splice(i, 0, queueElement);
added = true;
break ;
}
}
if (!added) {
this.items.push(queueElement);
}
}
}
// 列印佇列裡的元素
function print () {
var strArr = [];
strArr = this.items.map(function (item) {
return `${item.element}->${item.priority}`;
});
console.log(strArr.toString());
}
複製程式碼
最小優先佇列測試:
// 建立最小優先佇列minPriorityQueue例項
var minPriorityQueue = new MinPriorityQueue();
console.log(minPriorityQueue.isEmpty()); // true
minPriorityQueue.enqueue("John", 1); // undefined
minPriorityQueue.enqueue("Jack", 3); // undefined
minPriorityQueue.enqueue("Camila", 2); // undefined
minPriorityQueue.enqueue("Tom", 3); // undefined
minPriorityQueue.print(); // "John->1,Camila->2,Jack->3,Tom->3"
console.log(minPriorityQueue.size()); // 4
console.log(minPriorityQueue.isEmpty()); // false
minPriorityQueue.dequeue(); // {element: "John", priority: 1}
minPriorityQueue.dequeue(); // {element: "Camila", priority: 2}
minPriorityQueue.print(); // "Jack->3,Tom->3"
minPriorityQueue.clear(); // undefined
console.log(minPriorityQueue.size()); // 0
複製程式碼
實現最大優先佇列
最大優先佇列只要將優先順序的判斷改為大於號">"就可以了:
// 最大優先佇列MaxPriorityQueue類
function MaxPriorityQueue () {
this.items = [];
this.enqueue = enqueue;
this.dequeue = dequeue;
this.front = front;
this.isEmpty = isEmpty;
this.size = size;
this.clear = clear;
this.print = print;
}
// 優先佇列新增元素,要根據優先順序判斷在佇列中的插入順序
function enqueue (element, priority) {
var queueElement = {
element: element,
priority: priority
};
if (this.isEmpty()) {
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);
}
}
}
複製程式碼
最大優先佇列測試:
// 建立最大優先佇列maxPriorityQueue例項
var maxPriorityQueue = new MaxPriorityQueue();
console.log(maxPriorityQueue.isEmpty()); // true
maxPriorityQueue.enqueue("John", 1); // undefined
maxPriorityQueue.enqueue("Jack", 3); // undefined
maxPriorityQueue.enqueue("Camila", 2); // undefined
maxPriorityQueue.enqueue("Tom", 3); // undefined
maxPriorityQueue.print(); // "Jack->3,Tom->3,Camila->2,John->1"
console.log(maxPriorityQueue.size()); // 4
console.log(maxPriorityQueue.isEmpty()); // false
maxPriorityQueue.dequeue(); // {element: "Jack", priority: 3}
maxPriorityQueue.dequeue(); // {element: "Tom", priority: 3}
maxPriorityQueue.print(); // "Camila->2,John->1"
maxPriorityQueue.clear(); // undefined
console.log(maxPriorityQueue.size()); // 0
複製程式碼
2.3 迴圈佇列
還有一種佇列實現叫做迴圈佇列。
迴圈佇列的一個例子就是擊鼓傳花遊戲(Hot Potato)。在這個遊戲中,孩子們圍城一個圓圈,擊鼓的時候把花盡快的傳遞給旁邊的人。某一時刻擊鼓停止,這時花在誰的手裡,誰就退出圓圈直到遊戲結束。重複這個過程,直到只剩一個孩子(勝者)。
下面我們在普通佇列的基礎上,實現一個模擬的擊鼓傳花遊戲:
// 實現擊鼓傳花
function hotPotato (nameList, num) {
var queue = new Queue();
for (var i = 0; i < nameList.length; i++) {
queue.enqueue(nameList[i]);
}
var eliminated = '';
while (queue.size() > 1) {
// 迴圈num次,隊首出來去到隊尾
for (var i = 0; i < num; i++) {
queue.enqueue(queue.dequeue());
}
// 迴圈num次過後,移除當前隊首的元素
eliminated = queue.dequeue();
console.log(`${eliminated}在擊鼓傳花中被淘汰!`);
}
// 最後只剩一個元素
return queue.dequeue();
}
// 測試
var nameList = ["John", "Jack", "Camila", "Ingrid", "Carl"];
var winner = hotPotato(nameList, 10);
console.log(`最後的勝利者是:${winner}`);
複製程式碼
執行結果為:
// John在擊鼓傳花中被淘汰!
// Ingrid在擊鼓傳花中被淘汰!
// Jack在擊鼓傳花中被淘汰!
// Camila在擊鼓傳花中被淘汰!
// 最後的勝利者是:Carl
複製程式碼
三、結束
本文會同步到我的個人部落格,完整程式碼可以到我的github倉庫檢視,如果對你有幫助的話歡迎點一個Star~~