棧的介紹
棧就是和列表類似的一種資料結構,它可用來解決計算機世界裡的很多問題。棧是一種高 效的資料結構,因為資料只能在棧頂新增或刪除,所以這樣的操作很快,而且容易實現。 棧的使用遍佈程式語言實現的方方面面,從表示式求值到處理函式呼叫
棧是一種特殊的列表,棧內的元素只能通過列表的一端訪問,這一端稱為棧頂。咖啡廳內 的一摞盤子是現實世界中常見的棧的例子。只能從最上面取盤子,盤子洗淨後,也只能摞 在這一摞盤子的最上面。棧被稱為一種後入先出(LIFO,last-in-first-out)的資料結構。
由於棧具有後入先出的特點,所以任何不在棧頂的元素都無法訪問。為了得到棧底的元 素,必須先拿掉上面的元素。
對棧的兩種主要操作是將一個元素壓入棧和將一個元素彈出棧。入棧使用 push()
方法,出 棧使用 pop()
方法。圖 一演示了入棧和出棧的過程。
另一個常用的操作是預覽棧頂的元素。pop()
方法雖然可以訪問棧頂的元素,但是呼叫該方 法後,棧頂元素也從棧中被永久性地刪除了。peek()
方法則只返回棧頂元素,而不刪除它。
棧的實現
實現一個棧,當務之急是決定儲存資料的底層資料結構。這裡採用的是陣列。
我們的實現以定義 Stack 類開始:
class Stack {
constructor() {
this.dataStore = [];
this.top = 0;
}
push() { }
pop() { }
peek() { }
clear() { }
length() { }
empty() { }
toString(){ }
}
我們用陣列 dataStore
儲存棧內元素,建構函式將其初始化為一個空陣列。變數 top
記錄 棧頂位置,被建構函式初始化為 0,表示棧頂對應陣列的起始位置 0。如果有元素被壓入 棧,該變數的值將隨之變化。
實現push方法
先來實現 push()
方法。當向棧中壓入一個新元素時,需要將其儲存在陣列中變數 top
所對 應的位置,然後將 top
值加 1
,讓其指向陣列中下一個空位置。程式碼如下所示:
push(element) {
this.dataStore[++this.top] = element;
}
這裡要特別注意 ++
操作符的位置,它放在 this.top
的後面,這樣新入棧的元素就被放在 top
的當前值對應的位置,然後再將變數 top
的值加 1
,指向下一個位置。
實現pop方法
pop()
方法恰好與 push()
方法相反——它返回棧頂(被刪除的)元素,同時將變數 top
的值減 1
:
pop() {
return this.dataStore[this.top--];
}
實現peek方法
peek()
方法返回陣列的第 top-1
個位置的元素,即棧頂元素:
peek() {
return this.dataStore[this.top - 1];
}
如果對一個空棧呼叫 peek()
方法,結果為 undefined
。這是因為棧是空的,棧頂沒有任何 元素。
實現length方法
有時候需要知道棧記憶體儲了多少個元素。length()
方法通過返回變數 top
值的方式返回棧內的元素個數:
length() {
return this.top
}
實現clear方法
可以將變數 top
的值設為 0
,輕鬆清空一個棧:
clear() {
this.top = 0;
}
實現empty方法
empty()
方法可以知道一個Stack
是否是空的
empty() {
return this.top === 0;
}
使用Stack類解決問題
數制間的轉化
可以利用棧將一個數字從一種數制轉換成另一種數制。假設想將數字 n 轉換為以 b 為基數 的數字,實現轉換的演算法如下。
- 最高位為
n % b
,將此位壓入棧。 - 使用
n/b
代替n
。 - 重複步驟 1 和 2,直到 n 等於 0,且沒有餘數。
- 持續將棧內元素彈出,直到棧為空,依次將這些元素排列,就得到轉換後數字的字元 串形式
此演算法只針對基數為2~9的情況
程式碼實現
function mulBase(num, base=2) {
let tmp = new Stack();
while (num > 0) {
tmp.push(num % base)
num = Math.floor(num /= base);
}
let target = '';
while(tmp.length() > 0) {
target = target + tmp.pop();
}
return target;
}
console.log(mulBase(3,2)) // "11"
console.log(mulBase(3,6)) // "6"
迴文字串的判斷
迴文是指這樣一種現象:一個單詞、短語或數字,從前往後寫和從後往前寫都是一樣的。 比如,單詞“dad”、“racecar”就是迴文;如果忽略空格和標點符號,下面這個句子也是回 文,“A man, a plan, a canal: Panama”;數字 1001 也是迴文。
使用棧,可以輕鬆判斷一個字串是否是迴文。我們將拿到的字串的每個字元按從左至 右的順序壓入棧。當字串中的字元都入棧後,棧內就儲存了一個反轉後的字串,最後 的字元在棧頂,第一個字元在棧底。
function isPalindrome(word) {
if (typeof word !== "string") {
throw TypeError(`引數不是string型別`);
}
let tmp = new Stack();
for (let element of word) {
tmp.push(element)
}
let rword = '';
while (tmp.length() > 0) {
rword += tmp.pop()
}
console.log(rword);
return word === rword;
}
console.log(isPalindrome("racecar")) // true
console.log(isPalindrome("hello")) // false
參考資料
- 資料結構與演算法JavaScript描述