棧的介紹
棧就是和列表類似的一種數據結構,它可用來解決計算機世界里的很多問題。棧是一種高 效的數據結構,因為數據只能在棧頂添加或刪除,所以這樣的操作很快,而且容易實現。 棧的使用遍布程序語言實現的方方面面,從表達式求值到處理函數調用
棧是一種特殊的列表,棧內的元素只能通過列表的一端訪問,這一端稱為棧頂。咖啡廳內 的一摞盤子是現實世界中常見的棧的例子。只能從最上面取盤子,盤子洗凈后,也只能摞 在這一摞盤子的最上面。棧被稱為一種后入先出(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描述