若是你問我,人可否改變本身的命運,我也不曉得。但我曉得,不認命,就是咱們這代人的命。javascript
- 基礎知識就像是一座大樓的地基,它決定了咱們的技術高度。
- 咱們應該多掌握一些可移值的技術或者再過十幾年應該都不會過期的技術,數據結構與算法就是其中之一。
隊列被用在不少地方,好比提交操做系統執行的一系列進程、打印任務池等。java
隊列(queue) 只容許在表的一端進行插入,而在另外一端進行刪除元素。容許插入的一端稱爲 隊尾(rear) ,容許刪除的一端則稱爲 隊頭(front) 。隊列中沒有元素時稱爲 空隊列 。node
從數據存儲的角度看,實現隊列有兩種方式,一種是以數組作基礎,一種是以鏈表作基礎。算法
隊列方法:數組
操做 | 方法 |
---|---|
入隊 | enqueue(el) |
出隊 | dequeue() |
取隊頭元素 | front() |
判斷是否爲空隊列 | isEmpty() |
返回隊列大小 | size() |
清空隊列 | clear() |
這裏採用的是數組做爲其存儲數據的底層數據結構。數據結構
用數組 _data 保存隊列內元素,構造函數將其初始化爲一個空數組。函數
class Queue {
constructor() {
this._data = [];
}
enqueue(el) { // 添加新元素到隊尾
this._data.push(el);
}
dequeue() { // 移除隊頭元素,同時返回被移除的元素
return this._data.shift();
}
front() { // 查看隊頭元素
return this._data[0];
}
isEmpty() { // 判斷是否爲空隊列
return this._data.length === 0;
}
size() { // 查詢隊列的大小
return this._data.length;
}
clear() {
this._data = [];
}
// 非必重寫方法,僅供測試使用
// 重寫 JavaScript 對象默認的 toString() 方法
// toString() {
// return this._data.join(' -> ');
// }
}
複製代碼
這裏採用的是鏈表做爲其存儲數據的底層數據結構。測試
鏈隊是指採用鏈式存儲結構實現的隊列。一般鏈隊使用單鏈表來表示。this
function Queue() {
// 這裏使用 _front 指代頭指針,_rear 指代尾指針
this._front = this._rear = new this.__Node__('head');
this._len = 0;
}
Queue.prototype = {
constructor: Queue,
__Node__: function(el) {
this.data = el;
this.next = null;
},
enqueue: function(el) { // 添加新元素到隊尾
const __node__ = new this.__Node__(el);
this._rear.next = __node__;
this._rear = this._rear.next;
this._len++;
},
dequeue: function() { // 移除隊頭元素,同時返回被移除的元素
if(this._len === 0) return;
const __node__ = this._front.next;
this._front.next = __node__.next;
this._len--;
return __node__.data;
},
front: function() { // 查看隊頭元素
return this._front.next && this._front.next.data;
},
isEmpty: function() { // 判斷是否爲空隊列
return this._len === 0;
},
size: function() { // 查詢隊列的大小
return this._len;
},
clear: function() {
this._front = this._rear = new this.__Node__('head');
this._len = 0;
},
// 非必重寫方法,僅供測試使用
// 重寫 JavaScript 對象默認的 toString() 方法
// toString() {
// let curr = this._front.next,
// list = [];
// while(curr) {
// list.push(curr.data);
// curr = curr.next;
// }
// return list.join(' -> ');
// }
}
複製代碼
上述順序隊列、鏈隊測試用例:spa
const queue = new Queue();
queue.isEmpty(); // true
queue.enqueue('a'); // undefined
queue.enqueue('b'); // undefined
queue.enqueue('c'); // undefined
// queue.toString(); // "a -> b -> c"
queue.size(); // 3
queue.isEmpty(); // false
queue.dequeue(); // "a"
queue.dequeue(); // "b"
// queue.toString(); // "c"
queue.clear(); // undefined
queue.size(); // 0
複製代碼
在 傳統的隊列 中,底層數據結構使用的是數組進行存儲的狀況下(實際上,是使用了一組地址連續的存儲單元依次存放從隊列頭到隊列尾的元素),咱們須要指定隊列所能達到的最大長度;在 出隊 操做時,隊列的 頭指針(front) 指向數組的下一個元素(也就是下一段存儲空間),會形成實際狀況下,隊列中的元素總和,沒法達到隊列的最大長度,咱們稱這一現象爲 假溢出 。而循環隊列偏偏是解決假溢出的方案之一。
其實在實現順序隊列的時候,咱們已經藉助 JavaScript內置的API 間接的實現了咱們 循環隊列 的需求。
通常狀況下,從隊列中刪除的元素,必定是率先入隊的元素。可是也有一些使用隊列的應用,在刪除元素時沒必要遵循先進先出的約定。這種時候,就須要使用一種叫作 優先隊列 的數據結構來進行處理。
咱們假設,在將元素入隊時將 優先碼 一併入隊,而且優先碼按從小到大進行排序,優先碼越小,則表明優先級越高。下面簡單講 順序隊列 更改成 優先隊列 。
<!-- 在這裏咱們對上面的順序列表進行稍微改寫,使程序能知足咱們的需求 -->
class Queue {
...
enqueue(el, code = 3) { // 添加新元素到隊尾
const p = {
val: el,
code: code
}
let i = 0,
added = true;
while(i < this._data.length) {
if(code <= this._data[i].code) {
this._data.splice(i, 0, p);
added = false;
break;
}
i++
}
if(added) this._data.push(p);
}
dequeue() { // 移除隊頭元素,同時返回被移除的元素
return this._data.shift().val;
}
front() { // 查看隊頭元素
return this._data[0].val;
}
...
// 非必重寫方法,僅供測試使用
// 重寫 JavaScript 對象默認的 toString() 方法
// toString() {
// const ret = this._data.map(item => {
// return item.val;
// });
// return ret.join(' -> ');
// }
}
複製代碼
優先隊列測試用例:
var queue = new Queue();
queue.isEmpty(); // true
queue.enqueue("a", 1); // undefined
queue.enqueue("d", 3); // undefined
queue.enqueue("b", 2); // undefined
queue.enqueue("c", 3); // undefined
queue.toString(); // "a -> b -> c -> d"
queue.size(); // 4
queue.isEmpty(); // false
queue.dequeue(); // "a"
queue.dequeue(); // "b"
queue.toString(); // "c -> d"
queue.clear(); // undefined
queue.size(); // 0
複製代碼
隊列在程序設計中也常常出現。典型的例子就是操做系統中的做業排隊。
若是有錯誤或者不嚴謹的地方,請務必給予指正,十分感謝。