【資料結構】棧(Stack)和佇列(Queue)
棧(Stack)
堆疊遵循LIFO(後進先出)的原則
如果你把書堆疊起來,上面的書會比下面的書先拿。或者當你在網上瀏覽時,後退按鈕會引導你到最近瀏覽的頁面。
Stack具有以下常見方法:
方法:
- push 入棧
- pop 出棧
- peek 檢視頂部元素
- length 返回堆疊中元素的數量
- clear 清空棧
Javascript中的陣列具有Stack的屬性,但是我們使用 function Stack()從頭開始構建Stack
function Stack() {
this.count = 0;
this.storage = [];
// 入棧
this.push = (val) => {
this.storage[this.count] = val;
this.count++;
}
// 出棧 => 刪除棧頂元素,並返回
this.pop = () => {
if (this.count === 0) {
return undefined;
}
this.count--;
return this.storage[this.count];
}
// 檢視 => 檢視棧頂元素
this.peek = () => {
return this.storage[this.count - 1]
}
// 棧長度
this.length = () => {
return this.count;
}
// 清空棧
this.clear = () => {
this.storage = [];
this.count = 0;
}
}
// test
const stack = new Stack();
console.log(stack.peek()) // undefined
stack.push('Apple')
stack.push('Banana')
stack.push('Pear')
console.log(stack.length()) // 3
console.log(stack.peek()) // Pear
console.log(stack.pop()) // Pear
console.log(stack.length()) // 2
console.log(stack.pop()) // Banana
console.log(stack.pop()) // Apple
console.log(stack.pop()) // undefined
console.log(stack.length()) // 0
console.log(stack.peek()) // undefined
至此,我們已經可以用JS實現一個棧,但是你仍可能處於不知道如何正確使用的狀態,接下來,我們舉兩個例子,一起看看棧的使用。
判斷一個字串是不是迴文
迴文是指一個字串,從前往後寫和從後往前寫結果都是一樣的,比如單詞 level , racecar,就是迴文,數字 1001 也是迴文。
我們採用棧,可以很輕鬆判斷一個字串是否是迴文,實現演算法很簡單,相信你們都猜到了。
我們把字串從左到右依次壓入棧,這樣,棧中儲存了該字串反轉後的字元,我們再依次出棧,通過比較出棧後的字串是否與原字串是否相等,就可判斷該字串是否是迴文。
具體程式碼實現如下:
function isPalindrome(word) {
const stack = new Stack();
for (let i = 0; i < word.length; i++) {
stack.push(word[i])
}
let words = '';
while(stack.length() > 0) {
words += stack.pop();
}
return word == words;
}
console.log(isPalindrome('level')) // true
console.log(isPalindrome('1001')) // true
console.log(isPalindrome('word')) // false
佇列(Queue)
Queue與Stack類似。唯一不同的是,Queue使用的是FIFO原則(先進先出)。
換句話說,當你排隊等候公交車時,佇列中的第一個總是先上車。
方法
- enqueue 入隊
- dequeue 出隊
- front 檢視隊首元素
- back 檢視隊尾元素
- toString 顯示佇列所有元素
- empty 判斷佇列是否為空
- size 對列長度
- clear 清空佇列
function Queue() {
this.collection = [];
this.enqueue = (val) => {
this.collection.push(val)
}
this.dequeue = () => {
return this.collection.shift()
}
this.front = () => {
return this.collection[0]
}
this.back = () => {
return this.collection[this.collection.length - 1]
}
this.toString = () => {
return this.collection.join('-')
}
this.empty = () => {
return this.collection.length === 0;
}
this.size = () => {
return this.collection.length;
}
this.clear = () => {
this.collection = []
}
}
const queue = new Queue();
queue.enqueue('Libai')
queue.enqueue('看外面')
queue.enqueue('有飛機')
queue.enqueue('哈哈哈')
console.log(queue.toString()) // Libai-看外面-有飛機-哈哈哈
console.log(queue.back()) // 哈哈哈
console.log(queue.front()) // Libai
console.log(queue.dequeue()) // Libai
console.log(queue.front()) // 看外面
console.log(queue.empty()) // false
queue.clear()
console.log(queue.empty()) // true
console.log(queue.front()) // undefined
console.log(queue.dequeue()) // undefined
優先佇列
在一般情況下,從佇列中刪除的元素,一定是率先入隊的元素。但是也有一些使用佇列的應用,在刪除元素時不必遵守先進先出的約定。
這種應用,需要使用一個叫做優先佇列的資料結構來進行模擬。
優先佇列在生活中的例子: 比如普通使用者上醫院需要排隊掛號, 但是具有 VIP 的使用者能’插隊’辦理業務。
沒錯,有錢就是了不起(▽)
用程式碼實現如下:
// 重新定義enqueue方法
this.enqueue = (item, level) => {
const data = {item, level};
if (this.empty()) {
this.collection.push(data)
} else {
let add = false;
for (let i = 0; i < this.size(); i++) {
if (level < this.collection[i][1]) {
this.collection.splice(i, 0, data)
add = true;
break;
}
}
add && this.collection.push(data)
}
}
測試一下
const queue = new Queue();
queue.enqueue(['gannicus', 3])
queue.enqueue(['spartacus', 1])
queue.enqueue(['crixus', 2])
queue.enqueue(['oenomaus', 4])
console.log(queue.collection)
// [
// ['spartacus', 1]
// ['crixus', 2]
// ['gannicus', 3]
// ['oenomaus', 4]
// ]
可以看到都是按指定等級來排序的,沒錯,有錢就是了不起
迴圈佇列
迴圈佇列的一個例子就是擊鼓傳花遊戲
在這個遊戲中,孩子們圍成一個圓圈,把花盡快地傳遞給旁邊的人。
某一時刻傳花停止,這個時候花在誰手裡,誰就退出圓圈、結束遊戲。重複這個過程,直到只剩一個孩子(勝者)。
const drumGame = function(names, number) {
const queue = new Queue()
for (let i = 0; i < names.length; i++) {
queue.enqueue(names[i])
}
while (queue.size() > 1) {
for (let i = 0; i < number; i++) {
// 這句是迴圈佇列的核心
// 給定一個數字,然後迭代佇列。從佇列開頭移除一項,再將其新增到佇列末尾
// 模擬擊鼓傳花(如果你把花傳給了旁邊的人,你被淘汰的威脅就立刻解除了)
queue.enqueue(queue.dequeue())
}
const loser = queue.dequeue()
console.log(loser + ' 出局')
}
return queue.dequeue() // 留下的最後一個就是勝利者
}
const names = ['John', 'Jack', 'Camila', 'Ingrid', 'Carl']
const winner = drumGame(names, 7) // 假設每輪傳花 7 次
console.log('勝利者是: ' + winner)
// Camila 出局
// Jack 出局
// Carl 出局
// Ingrid 出局
// 勝利者是: John
實現基數排序
基數排序(radix sort)屬於“分配式排序”(distribution sort),它是透過鍵值的部份資訊,將要排序的元素分配至某些“桶”中,藉以達到排序的作用,基數排序法是屬於穩定性的排序,其時間複雜度為O (nlog®m),其中r為所採取的基數,而m為堆數,在某些時候,基數排序法的效率高於其它的穩定性排序法。
先看一下基數排序的的實現步驟(以兩位數為例),需要掃描兩次,第一次按個位數字進行排序,第二次按十位數字排序,每個數字根據對應的數值分配到到不同的盒子裡,最後將盒子的數字依次取出,組成新的列表即為排序好的數字。
假設我們有一串數字
73, 22, 93, 43, 55, 14, 28, 65, 39, 81
經過基數排序第一次掃描之後,數字被分配到如下盒子中:
Bin 0:
Bin 1: 81
Bin 2: 22
Bin 3: 73, 93, 43
Bin 4: 14
Bin 5: 55, 65
Bin 6:
Bin 7:
Bin 8: 28
Bin 9: 39
根據盒子的順序,對數字進行第一次排序的結果如下
81, 22, 73, 93, 43, 14, 55, 65, 28, 39
然後根據十位數字排序,再放到不同的盒子裡
Bin 0:
Bin 1: 14
Bin 2: 22, 28
Bin 3: 39
Bin 4: 43
Bin 5: 55
Bin 6: 65
Bin 7: 73
Bin 8: 81
Bin 9: 93
接下來將這些盒子中的數值重新串接起來,整個數列已經排序完畢
14, 22, 28, 39, 43, 55, 65, 73, 81, 93
唉,開始寫程式碼~~
拿起鍵盤就是一頓噼裡啪啦~~ 噼裡啪啦~~ 梭哈~~
/**
* COPY 未驗證 暫定
*/
//基數排序
var queues = []; //定義佇列陣列
var nums = []; //定義數字陣列
//選十個0~99的隨機數進行排序
for ( var i = 0 ; i < 10 ; i ++ ){
queues[i] = new Queue();
nums[i] = Math.floor( Math.random() * 101 );
}
//排序之前
console.log( 'before radix sort: ' + nums );
//基數排序
distribution( nums , queues , 10 , 1 );
collect( queues , nums );
distribution( nums , queues , 10 , 10 );
collect( queues , nums );
//排序之後
console.info('after radix sort: ' + nums );
//根據相應的(個位和十位)數值,將數字分配到相應佇列
function distribution ( nums , queues , n , digit ) { //digit表示個位或者十位的值
for( var i = 0 ; i < n ; i++ ){
if( digit == 1){
queues[ nums[i] % 10 ].enqueue( nums[i] );
}else{
queues[ Math.floor( nums[i] / 10 ) ].enqueue( nums[i] );
}
}
}
//從佇列中收集數字
function collect ( queues , nums ) {
var i = 0;
for ( var digit = 0 ; digit < 10 ; digit ++ ){
while ( !queues[digit].empty() ){
nums[ i++ ] = queues[digit].front();
queues[digit].dequeue();
}
}
}
相關文章
- Stack and Queue in JavaScript(Javascript中的資料結構之棧和佇列)JavaScript資料結構佇列
- 資料結構—棧和佇列資料結構佇列
- 資料結構(棧和佇列)資料結構佇列
- js資料結構--佇列(queue)JS資料結構佇列
- 資料結構之佇列(Queue)資料結構佇列
- java集合類——Stack棧類與Queue佇列Java佇列
- 資料結構-佇列、棧資料結構佇列
- 資料結構二之棧和佇列資料結構佇列
- 資料結構-棧與佇列資料結構佇列
- 資料結構-js實現棧和佇列資料結構JS佇列
- 【資料結構】棧和佇列的總結對比資料結構佇列
- 畫江湖之資料結構【第二話:佇列和棧】佇列資料結構佇列
- 畫江湖之資料結構 [第二話:佇列和棧] 佇列資料結構佇列
- 學習JavaScript資料結構(一)——棧和佇列JavaScript資料結構佇列
- JavaScript資料結構之陣列棧佇列JavaScript資料結構陣列佇列
- js資料結構--棧(stack)JS資料結構
- 畫江湖之資料結構【第二話:佇列和棧】棧資料結構佇列
- 畫江湖之資料結構 [第二話:佇列和棧] 棧資料結構佇列
- 資料結構基礎學習之(棧和佇列)資料結構佇列
- 《資料結構與演算法》——表、棧和佇列資料結構演算法佇列
- 大二資料結構學習3(棧和佇列)資料結構佇列
- 三、資料結構演算法-棧、佇列、優先佇列、雙端佇列資料結構演算法佇列
- php實現基本資料結構之棧、佇列PHP資料結構佇列
- 資料結構與演算法-棧與佇列資料結構演算法佇列
- python資料結構與演算法——棧、佇列與雙端佇列Python資料結構演算法佇列
- 資料結構分析及其實現(Stack、Queue、Tree、LinkedList)資料結構
- 結構與演算法(02):佇列和棧結構演算法佇列
- 重溫四大基礎資料結構:陣列、連結串列、佇列和棧資料結構陣列佇列
- Python資料結構與演算法系列四:棧和佇列Python資料結構演算法佇列
- 一本正經的聊資料結構(3):棧和佇列資料結構佇列
- 資料結構-佇列資料結構佇列
- 【資料結構-----佇列】資料結構佇列
- 資料結構 - 佇列資料結構佇列
- 資料結構:特殊的線性表之 棧 & 佇列資料結構佇列
- 資料結構與演算法(二)佇列、棧、連結串列資料結構演算法佇列
- C#資料結構與演算法2-C# 棧和佇列C#資料結構演算法佇列
- Java版-資料結構-佇列(陣列佇列)Java資料結構佇列陣列
- 線性結構 佇列與棧佇列