讀古今文學網 > 學習JavaScript數據結構與算法(第2版) > 3.1 棧數據結構 >

3.1 棧數據結構

棧是一種遵從後進先出(LIFO)原則的有序集合。新添加的或待刪除的元素都保存在棧的同一端,稱作棧頂,另一端就叫棧底。在棧裡,新元素都靠近棧頂,舊元素都接近棧底。

在現實生活中也能發現很多棧的例子。例如,下圖裡的一摞書或者餐廳裡堆放的盤子。

棧也被用在編程語言的編譯器和內存中保存變量、方法調用等。

3.1.1 創建棧

我們將創建一個類來表示棧。讓我們從基礎開始,先聲明這個類:

function Stack {
  //各種屬性和方法的聲明
}

  

首先,我們需要一種數據結構來保存棧裡的元素。可以選擇數組:

let items = ;

  

接下來,要為我們的棧聲明一些方法。

  • push(element(s)):添加一個(或幾個)新元素到棧頂。

  • pop:移除棧頂的元素,同時返回被移除的元素。

  • peek:返回棧頂的元素,不對棧做任何修改(這個方法不會移除棧頂的元素,僅僅返回它)。

  • isEmpty:如果棧裡沒有任何元素就返回true,否則返回false

  • clear:移除棧裡的所有元素。

  • size:返回棧裡的元素個數。這個方法和數組的length屬性很類似。

3.1.2 向棧添加元素

我們要實現的第一個方法是push。這個方法負責往棧裡添加新元素,有一點很重要:該方法只添加元素到棧頂,也就是棧的末尾。push方法可以這樣寫:

this.push = function(element){
  items.push(element);
};

  

因為我們使用了數組來保存棧裡的元素,所以可以用上一章裡學到的數組的push方法來實現。

3.1.3 從棧移除元素

接著,我們來實現pop方法。這個方法主要用來移除棧裡的元素。棧遵從LIFO原則,因此移出的是最後添加進去的元素。因此,我們可以用上一章講數組時介紹的pop方法。棧的pop方法可以這樣寫:

this.pop = function{
  return items.pop;
};

  

只能用pushpop方法添加和刪除棧中元素,這樣一來,我們的棧自然就遵從了LIFO原則。

3.1.4 查看棧頂元素

現在,為我們的類實現一些額外的輔助方法。如果想知道棧裡最後添加的元素是什麼,可以用peek方法。這個方法將返回棧頂的元素:

this.peek = function{
  return items[items.length-1];
};

  

因為類內部是用數組保存元素的,所以訪問數組的最後一個元素可以用 length - 1

在上圖中,有一個包含三個元素的棧,因此內部數組的長度就是3。數組中最後一項的位置是2,length - 1(3-1)正好是2。

3.1.5 檢查棧是否為空

下一個要實現的方法是 isEmpty,如果棧為空的話將返回true,否則就返回false

this.isEmpty = function{
  return items.length == 0;
};

  

使用isEmpty方法,我們能簡單地判斷內部數組的長度是否為0。

類似於數組的length屬性,我們也能實現棧的length。對於集合,最好用size代替length。因為棧的內部使用數組保存元素,所以能簡單地返回棧的長度:

this.size = function{
  return items.length;
};

  

3.1.6 清空和打印棧元素

最後,我們來實現clear方法。clear方法用來移除棧裡所有的元素,把棧清空。實現這個方法最簡單的方式是:

this.clear = function{
  items = ;
};

  

另外也可以多次調用pop方法,把數組中的元素全部移除,這樣也能實現clear方法。

完成了!棧已經實現。通過一個例子來放鬆一下:為了檢查棧裡的內容,我們來實現一個輔助方法,叫print。它會把棧裡的元素都輸出到控制台:

this.print = function{
  console.log(items.toString);
};

  

這樣,我們就完整創建了棧!

使用Stack

在深入瞭解棧的應用前,我們先來學習如何使用Stack類。

首先,我們需要初始化Stack類。然後,驗證一下棧是否為空(輸出是 true,因為還沒有往棧裡添加元素)。

let stack = new Stack;
console.log(stack.isEmpty); //輸出為true

  

接下來,往棧裡添加一些元素(這裡我們添加數字5和8;你可以添加任意類型的元素):

stack.push(5);
stack.push(8);

  

如果調用peek方法,將會輸出8,因為它是往棧裡添加的最後一個元素:

console.log(stack.peek); //輸出8

  

再添加一個元素:

stack.push(11);
console.log(stack.size); //輸出3
console.log(stack.isEmpty); //輸出false

  

我們往棧裡添加了11。如果調用size方法,輸出為3,因為棧裡有三個元素(5、8和11)。如果我們調用isEmpty方法,會看到輸出了false(因為棧裡有三個元素,不是空棧)。最後,我們再添加一個元素:

stack.push(15);

  

下圖描繪了目前為止我們對棧的操作,以及棧的當前狀態:

然後,調用兩次pop方法從棧裡移除2個元素:

stack.pop;
stack.pop;
console.log(stack.size); //輸出2
stack.print; //輸出[5, 8]

  

在兩次調用pop方法前,我們的棧裡有四個元素。調用兩次後,現在棧裡僅剩下5和8了。下圖描繪這個過程的執行: