列表是一種最天然的數據組織方式。上一章已經介紹如何使用List類將數據組織成一個列表。若是數據存儲的順序不重要。也沒必要對數據進行查找,那麼列表就是一種再好不過的數據結構。對於其它的一些應用,列表就顯得有些簡陋了。咱們須要某種和列表相似但更復雜的數據結構。javascript
棧就是和列表相似的一種數據結構,它能夠用來解決計算機世界裏不少的問題。棧是一種高效的數據結構,由於數據只能在棧頂添加或刪除,因此這樣的操做很快,並且容易實現。棧的使用遍及程序語言的方方面面,從表達式求值處處理函數調用。html
一:對棧的操做java
棧是一種特殊列表,棧內的元素只能經過列表的一端訪問,這一端被稱爲棧頂。咖啡廳的一摞盤子是現實世界中最多見的棧的例子。只能從上面取盤子,盤子洗淨後,也只能摞在這一摞盤子的最上面。棧被稱爲一種後入先出(LIFO,last-in-first-out)的數據結構。算法
因爲棧具備後入先出的特色,全部任何不在棧頂的元素都沒法訪問,爲了拿到棧底的元素,必選先拿掉上面的元素。如圖:編程
對於棧的兩種主要操做是將一個元素壓入棧和將一個元素彈出棧,入棧使用push()方法。出棧使用pop()方法。數組
另一個經常使用操做是預覽棧頂的元素。pop()方法雖然能夠訪問棧頂的元素,可是調用該方法後,棧頂的元素也從棧中永久的刪除了。peek()方法則只返回棧頂元素,而不刪除它。數據結構
爲了記錄棧頂元素的位置,同時也爲了標記哪裏能夠增長新的元素。咱們使用變量top,當向棧內壓入元素時,該變量增大,從棧內彈出元素時,該變量減少。編程語言
push(),pop()和peek()是棧的三個主要操做方法,可是棧還有其餘的方法和屬性。clear()方法清除棧內全部元素,length屬性記錄棧內元素的個數。咱們還定義了一個empty屬性,用於表示棧內是否還有元素(使用length也能夠達到一樣的目的)。函數
二:棧的實現:post
實現一個棧,當務之急是決定存儲數據的底層數據結構。這裏採用的是數組。
咱們實現以Stack類的構造函數開始:
function Stack() { this.dataStore = []; this.top = 0; this.push = push; this.pop = pop; this.peek = peek; }
咱們使用數組dataStore保存棧內元素,構造函數將其初始化爲一個空數組,變量top記錄棧頂的位置,被構造的函數初始化爲0,表示棧頂對於數組的起始位置爲0.若是有元素壓入棧,該變量的值隨之變化。
先來實現push()方法。當向棧內壓入一個新元素時,須要將其保存在數組中top所對應的位置,而後將top加1.讓其指向數組中的下一個空位置。
function push (element) { this.dataStore[this.top++] = element; }
這裏特別要注意++操做符的位置,它放在this.top的後面,這樣新入棧的元素就被放在top的當前值對應的位置,而後再將top值加1.指向下一個位置。
pop()方法剛好與push()方法相反,它返回棧頂元素,同時將變量top的值減1.
function pop() { return this.dataStore[--this.top] }
peek()方法返回數組的第top-1個位置的元素,即棧頂元素:
function peek() { return this.dataStore[this.top - 1] }
若是對空數組調用peek()方法,結果爲undefined。這是由於棧是空的,棧頂沒有任何元素。
有時候須要知道棧內存儲了多個元素。length()方法經過返回變量top值返回棧內元素的個數;
function length() { return this.top }
最後,能夠將top的值設置爲0,輕鬆清空一個棧。
function clear() { this.top = 0; }
附:測試代碼
function Stack() { this.dataStore = []; this.top = 0; this.push = push; this.pop = pop; this.peek = peek; this.clear = clear; this.length = length; } //壓入棧 function push (element) { this.dataStore[this.top++] = element; } //彈出棧 function pop() { return this.dataStore[--this.top] } //棧頂元素 function peek() { // if (this.dataStore[this.top - 1] == undefined) { // return "none" // } return this.dataStore[this.top - 1] } //清空棧 function clear() { this.top = 0; } //棧元素個數 function length() { return this.top } var newstak = new Stack(); newstak.push("牛牛") newstak.push("豆豆") newstak.push("花花") console.log(newstak.length()) console.log(newstak.peek()) var poped = newstak.pop(); console.log(poped);// console.log(newstak.peek());// newstak.clear(); console.log(newstak.length()) console.log(newstak.peek()); newstak.push("羊羊"); console.log(newstak); console.log(newstak.peek());
三.使用Stack類
有一些問題特別適合用棧來解決,先介紹幾個例子:
1.數制間互相轉換
能夠利用棧將一個數字從一種數值轉換成另外一種數制。假設想將數字n轉換爲以b爲基數的數字。實現轉換算法以下:
使用棧,在javascript中實現該算法就很容易,下面就是該函數的定義,能夠將數字轉化爲二至九進制的數字。
function mulBase(num, base) { var s = new Stack(); do { s.push(num % base); num = Math.floor(num /= base); } while (num > 0); var converted = ""; while(s.length() > 0) { converted += s.pop() } return converted; }
下面展現了若是使用該方法將數字轉換爲二進制和八進制數。
將數字轉換爲二進制和八進制。
function mulBase(num, base) { var s = new Stack(); do { s.push(num % base); num = Math.floor(num /= base); } while (num > 0); var converted = ""; while(s.length() > 0) { converted += s.pop() } return converted; } var num = 32; var base = 2; var newNum = mulBase(num, base); //32 converted to base 2 is 100000 console.log(num + " converted to base " + base + " is " + newNum) num = 125; base = 8; var newNum = mulBase(num, base); //125 converted to base 8 is 175 console.log(num + " converted to base " + base + " is " + newNum)
2.迴文。
迴文是這樣一種現象:一個單詞,短語或數字,從前日後寫和日後寫都是同樣的。好比: 單詞"dad","racecar"就是迴文。若是忽略空格和標點符號,下面的句子也是迴文。「A man, a plan, a canal:Panama」; 數字1001也是迴文。
使用棧,能夠輕鬆判斷一個字符串是否迴文。咱們將拿到的自字符串的每一個字符按照從左至右的順序壓入棧。當字符串的字符都入棧後,棧內就保存了一個反轉的字符串,最後的字符串在棧頂,第一個字符串在棧底。
字符串完整壓入棧內後,經過持續彈出棧中的每一個字母就能夠獲得一個新字符串,該字符串恰好與原來的字符串順序相反。咱們只需比較兩個字符串便可。若是他們相等,就是一個迴文。
例子:判斷給定字符串是否迴文。
function isPalindrome(word) { var s = new Stack(); for (var i = 0; i < word.length; ++i) { s.push(word[i]); } var word = ""; while(s.length() > 0) { rword += s.pop(); } if (word == rword) { return true; } else { return false; } } var word = "hello"; if (isPalindrome(word)) { console.log(word + " 是迴文的"); } else { console.log(word + " 不是迴文的") } var word = "racecar"; if (isPalindrome(word)) { console.log(word + " 是迴文的"); } else { console.log(word + " 不是迴文的") }
三:遞歸演示;
棧經常使用來實現編程語言,使用棧實現遞歸即爲一例(這裏只用棧來模擬遞歸過程)。
爲了演示如何用棧實現遞歸,考慮如下求階乘的遞歸定義。首先看看5的階乘是如何定義的
5! = 5*4*3*2*1 = 120
下面是一個遞歸函數,能夠計算任何數字的階乘
function factorial(n) { if (n === 0) { return 1; } else { return n * factorial(n - 1) } }
使用棧來模擬計算5的階乘,返回120
使用棧來模擬計算5!的過程,首先將數字從5到1壓入棧,而後使用一個循環,將數字彈出連乘,就獲得了5 的階乘
function fact(n) { var s = new Stack(); while (n > 1) { s.push(n--); } var product = 1; while(s.length() > 0) { product *= s.pop() } return product; }
(本章完結)
上一章:第三章:javascript: 列表 下一章:第五章: 隊列