什麼是棧(Stack)
- 棧是一種遵從後進先出(LIFO)原則的有序集合。
- 新新增的或待刪除的元素都儲存在棧的末尾,稱為棧頂,另一端叫棧底。
- 在棧裡,新元素都靠近棧頂,舊元素都接近棧底
現實中的例子
在生活中也能發現很多棧的例子。例如,廚房裡堆放的盤子,總是疊在上方的先被使用;輸入框內容進行刪除時,總是最後輸入的先刪除;彈夾中的子彈,越後裝入的,越先發射......
手動實現一個棧
首先,建立一個類來表示棧
function Stack () { }
複製程式碼
我們需要選擇一種資料結構來儲存棧裡的元素,可以選擇陣列
function Stack(){
var items = []; //用來儲存棧裡的元素
}
複製程式碼
接下來,為棧新增一些方法
push(element(s)); //新增新元素到棧頂
pop(); //移除棧頂的元素,同時返回被移除的元素
peek(); //返回棧頂的元素,不對棧做任何修改
isEmpty(); //如果棧裡沒有任何元素就返回true,否則false
clear(); //移除棧裡的所有元素
size(); //返回棧裡的元素個數,類似於陣列的length屬性
複製程式碼
我們需要實現的第一個方法時push
。用來往棧裡新增新元素,有一點很重要:該方法只新增到棧頂,也就是棧的末尾。所以,可以這樣寫:
this.push = function (element) {
items.push(element);
}
複製程式碼
利用陣列的push方法,就可以實現在棧頂末尾新增新的元素了。
接著,來實現pop
方法,用來實現移除棧裡的元素。棧遵從LIFO(後進先出)原則。移出去的是最後新增進去的元素。因此,可以使用陣列的pop方法。
this.pop = function () {
return items.pop();
}
複製程式碼
這樣一來,這個棧自然就遵從了LIFO原則
現在,再來為這個棧添額外的輔助方法。
如果想知道棧裡最後新增的元素是什麼,可以用peek
方法。這個方法將返回棧頂的元素
this.peek = function () {
return items[items.length-1];
}
複製程式碼
因為類內部是用陣列儲存元素的,所以這裡訪問陣列最後一個元素用length-1
下一個要實現的方法是isEmpty
,如果棧為空的話,就返回true,否則返回false:
this.isEmpty = function () {
return items.length == 0;
}
複製程式碼
使用isEmpty方法,就能簡單地判斷棧內部是否為空。
類似於陣列地length屬性,我們也可以實現棧地length。
this.size = function () {
return items.length;
}
複製程式碼
因為棧地內部使用陣列儲存元素,所以陣列地length就是棧的長度。
實現clear
方法,clear方法用來清空棧中所有的元素。最簡單的實現方法是:
this.clear = function () {
items = [];
}
複製程式碼
其實多次呼叫pop方法也可以,但是沒有這個方法來的簡單快捷。
最後,為了檢查棧裡的內容,還需要實現一個輔助方法:print
。它會把棧裡的元素都輸出到控制檯:
this.print = function () {
console.log(items.toString());
}
複製程式碼
至此,我們就完整地建立了一個棧!
棧的完整程式碼
function Stack(){
var items = []; //用來儲存棧裡的元素
this.push = function (element) {
items.push(element);
}
this.pop = function () {
return items.pop();
}
this.peek = function () {
return items[items.length-1];
}
this.isEmpty = function () {
return items.length == 0;
}
this.size = function () {
return items.length;
}
this.clear = function () {
items = [];
}
this.print = function () {
console.log(items.toString());
}
}
複製程式碼
使用Stack類
棧已經建立好了,我們來測試一下
首先,來初始化Stack類。然後,驗證一下棧是否為空
var stack = new Stack();
console.log(stack.isEmpty()); //控制檯輸出true
複製程式碼
接下來,往棧裡面新增一下元素:
stack.push(5);
stack.push(8);
複製程式碼
如果呼叫peek方法,很顯然將會輸出8,因為它是棧頂的元素:
console.log(stack.peek()); //控制檯輸出8
複製程式碼
再新增一個元素:
stack.push(11);
console.log(stack.size()); //控制檯輸出3
複製程式碼
我們往棧裡又新增了11。如果呼叫size方法,輸出為3,因為棧裡有三個元素(5,8和11)。如果這時候呼叫isEmpty方法,會看到輸出了false(因為此時棧不為空)。最後,再來往裡面新增一個元素:
stack.push(15);
複製程式碼
然後,呼叫兩次pop方法從棧裡移除兩個元素:
stack.pop();
stack.pop();
console.log(stack.size()); //控制檯輸出2
stack.print(); //控制檯輸出[5,8]
複製程式碼
到這裡,整個棧的功能測試完成。
用棧來解決問題
使用棧來完成進位制轉換。
現實生活中,我們主要用10進位制,但在計算科學中,二進位制非常重要,因為計算機裡所有的內容都是用二進位制數字0和1來表示的。大學的計算機課都會先教進位制轉換。以二進位制為例:
function divideBy2 (decNumber) {
var remStack = new Stack(),
rem,
binaryString = '';
while (decNumber>0) { //{1}
rem = Math.floor(decNumber % 2); //{2}
remStack.push(rem); //{3}
decNumber = Math.floor(decNumber / 2); //{4}
}
while (!remStack.isEmpty()) { //{5}
binaryString += remStack.pop().toString();
}
return binaryString;
}
複製程式碼
這段程式碼裡,當結果滿足和2做整除的條件時,(行{1}),我們會獲得當前結果和2的餘數,放到棧裡(行{2}、{3})。然後讓結果和2做整除(行{4})
注:JavaScript有數字型別,但是它不會區分時整數還是浮點數。因此,要使用Math.floor函式讓除法的操作僅返回整數部分。
最後,用pop方法把棧中的元素都移除,把出棧的元素連線成字串(行{5})。
測試一下:
console.log(divideBy2(520)); //輸出1000001000
console.log(divideBy2(10)); //輸出1010
console.log(divideBy2(1000)); //輸出1111101000
複製程式碼
接下來,可以很容易的修改上面的演算法,使它能夠把十進位制轉化為任何進位制。除了讓十進位制數字和2整除轉成二進位制數,還可以傳入其他任意進位制的基數作為引數,就像下面的演算法這樣:
function baseConverter (decNumber, base) {
var remStack = new Stack(),
rem,
baseString = '';
digits = '0123456789ABCDEF'; //{6}
while (decNumber>0) {
rem = Math.floor(decNumber % base);
remStack.push(rem); //{3}
decNumber = Math.floor(decNumber / base);
}
while (!remStack.isEmpty()) {
baseString += digits[remStack.pop()]; //{7}
}
return baseString;
}
複製程式碼
在將十進位制轉成二進位制時,餘數是0或1;在將十進位制轉成八進位制時,餘數時0-8之間的數;但是將十進位制轉成十六進位制時,餘數時0-9之間的數字加上A、B、C、D、E、F(對應10、11、12、13、14和15)。因此,需要對棧中的數字做個轉化才可以(行{6}、{7})。
來測試一下輸出結果:
console.log(baseConverter(1231,2)); //輸出10011001111
console.log(baseConverter(1231,8)); //輸出2317
console.log(baseConverter(1231,16)); //輸出4CF
複製程式碼
顯然是正確的。
小結
我們用js程式碼完成了棧的模擬,而不是真正從底層去實現了棧。我們的目的是更好的去理解棧的用法,並且通過進位制轉換的例子來實際應用了它。棧的應用例項還有很多,比如平衡圓括號和漢諾塔。感興趣可以自行百度去了解