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

一隻菜鳥攻城獅啊發表於2020-08-26

定義

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

模擬實現

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

相關文章