1. 程式人生 > 實用技巧 >棧的模擬實現及常見演算法

棧的模擬實現及常見演算法

定義

棧是一種特殊的線性表,它只能在一個表的一個固定端進行資料結點的插入和刪除操作。棧按照後進先出的原則來儲存資料,也就是說,先插入的資料將被壓入棧底,最後插入的資料在棧頂,讀出資料時,從棧頂開始逐個讀出。棧在組合語言程式中,經常用於重要資料的現場保護。棧中沒有資料時,稱為空棧。

模擬實現

class Stack{
    constructor(){
    this.stack = [];
    this.top = 0;
    this.max=10000;
  };
  // 入棧
  push(item){
    if(this.top<this.max){
      this
.top++; this.stack.push(item); }else{ consle.error('棧溢位') } } // 出棧 pop(){ if(this.top > 0) { let x = this.stack.pop(); this.top--; return x; }else{ console.log('棧已經為空') } } // 判斷棧是否為空 empty(){ return this.top === 0; } // 返回位於棧頂的元素
peek(){ return this.stack[this.top]; } // 棧的大小 size(){ return this.top; } }

常見應用

進位制轉換

利用棧將轉化數字的進位制,假設將數字n轉換為以b為基數的數字,方法如下:

  1. 最高位為n % b,將此位壓入棧
  2. 使用Math.floor(n/b)代替n
  3. 重複步驟1和2,直到n等於0,且沒有餘數
  4. 持續將棧內元素彈出,直到棧為空
  function mulBase(num, base){
   var stack = new Stack();
   do {
      stack.push(num 
% base); num = Math.floor(num /= base) } while(num > 0) var str = ''; while(stack.length() > 0){ str += stack.pop(); } return str }

迴文判斷

利用棧,可以輕鬆判斷一個字串是否是迴文(迴文指一個字串從前往後寫和從後往前寫都一樣)

   function isPalindrome(word){
       var stack = new Stack();
       for(var i = 0, len = word.length; i++){
           stack.push(word[i]);
       }
       var rword = '';
       while(stack.length() > 0){
           rword += stack.pop();
       }
       return rword == word;
   }

當然正常我們直接使用

  var arr = Array.prototype.slice.call(word);
  return arr.reverse().join('') == word

實現特殊棧

實現一個特殊的棧,有棧的正常方法,能返回棧裡的最小值。要求時間複雜度為O(1)

思路:建立兩個棧,一個棧 data 放正常的資料。另一個棧 mins 放當前資料中的最小值。例如:若新新增的資料小於當前的最小值,兩個棧都新增新的資料。若新新增的資料大於當前棧中的最小值,mins 仍然添加當前最小值。

而且,data出資料的時候,mins同時出棧。

常見的演算法

有效的括號

leetCode 20:給定一個只包括 '(',')','{','}','[',']'的字串,判斷字串是否有效。

有效字串需滿足:左括號必須用相同型別的右括號閉合。左括號必須以正確的順序閉合。

var isValid = function(s) {
  let map = {
    '(': -1,
    ')': 1,
    '[': -2,
    ']': 2,
    '{': -3,
    '}': 3
  }
  let stack = []

  for (let i = 0; i < s.length; i++) {
    if (map[s[i]] < 0) {
      stack.push(s[i])
    } else {
      let last = stack.pop()
      if (map[last] + map[s[i]] != 0) return false
    }
  }
  if (stack.length > 0) return false
  return true
}

每日溫度

LeetCode 739: 根據每日氣溫列表,請重新生成一個列表,對應位置的輸出是需要再等待多久溫度才會升高超過該日的天數。如果之後都不會升高,請在該位置用 0 來代替。

例如,給定一個列表 temperatures = [73, 74, 75, 71, 69, 72, 76, 73],你的輸出應該是 [1, 1, 4, 2, 1, 1, 0, 0]。

const dailyTemperatures = function(T) {
    const len = T.length // 快取陣列的長度 
    const stack = [] // 初始化一個棧   
    const res = (new Array(len)).fill(0) //  初始化結果陣列,注意陣列定長,佔位為0
    for(let i=0;i<len;i++) {
      // 若棧不為0,且存在打破遞減趨勢的溫度值
      while(stack.length && T[i] > T[stack[stack.length-1]]) {
        // 將棧頂溫度值對應的索引出棧
        const top = stack.pop()  
        // 計算 當前棧頂溫度值與第一個高於它的溫度值 的索引差值
        res[top] = i - top 
      }
      // 注意棧裡存的不是溫度值,而是索引值,這是為了後面方便計算
      stack.push(i)
    }
    // 返回結果陣列
    return res 
};

圖解連結:

https://mp.weixin.qq.com/s/3kDSOHyd-qOw7apzj0Z9YQ

其他

利用堆疊,還可以解決如下常見問題:

  • 求解算術表示式的結果(LeetCode 224、227、772、770)
  • 求解直方圖裡最大的矩形區域(LeetCode 84)

參考連結

https://github.com/lznbuild/my-blog/issues/26

https://mp.weixin.qq.com/s/3kDSOHyd-qOw7apzj0Z9YQ