每日leetcode——155. 最小棧

Ethan發表於2022-03-16

題目

設計一個支援 push ,pop ,top 操作,並能在常數時間內檢索到最小元素的棧。

輸入:
["MinStack","push","push","push","getMin","pop","top","getMin"]
[[],[-2],[0],[-3],[],[],[],[]]

輸出:
[null,null,null,null,-3,null,0,-2]

思路

輔助棧

題目要求在常熟時間內能檢索到最小元素,常數時間就是O(1)時間複雜度,因此不能使用for迴圈遍歷檢索,for迴圈的時間複雜度是O(n)

思路是再維護一個輔助棧 minStack, 讓輔助棧的棧頂,始終儲存的是當前棧中的最小元素

class MinStack:

  def __init__(self):
    # 普通棧
    self.stack = []
    # 輔助棧,初始化給一個無窮大
    self.min_stack = [math.inf]

  def push(self, val: int) -> None:
    # 普通棧正常push
    self.stack.append(val)
    # 輔助棧只push小於等於棧頂的
    self.min_stack.append(min(val,self.min_stack[-1]))

  def pop(self) -> None:
    self.stack.pop()
    self.min_stack.pop()

  def top(self) -> int:
    return self.stack[-1]

  def getMin(self) -> int:
    return self.min_stack[-1]

不使用輔助棧

只用一個棧也可以解決問題。
輔助棧的方法,是新增一個棧用來維護最小值。
而只用一個棧,就只需要新增一個變數來儲存最小值。在資料出入棧的同時,通過一些方法,將最小值的變化記錄在資料棧中。

方法一:
在元素入棧時,如果入棧值比目前的最小值還小,那麼儲存最小值的變數就會被更新,之前的最小值,我們也push進入棧中儲存,然後在入棧新的元素。

入棧 3 
|   |   min = 3
|   |     
|_3_|    
stack   

入棧 5 
|   |   min = 3
| 5 |     
|_3_|    
stack  

入棧 2 
| 2 |   min = 2?
| 5 |     
|_3_|    
stack  

入棧2時,棧中最小值變成了2,如果直接將min跟新成2,那之前的最小值3就會丟失

為了儲存之前的最小值3,在入棧2時,先將之前最小值3入棧儲存在棧中,然後在入棧2,更新min

也就是,當新入棧的值,比之前的最小值還小,就將舊的最小值先儲存到棧中,在入棧新的值,並且更新最小值為新入棧的值

| 2 |   min = 2
| 3 |  
| 5 |     
|_3_|    
stack  

入棧 6 
| 6 |  min = 2
| 2 |   
| 3 |  
| 5 |     
|_3_|    
stack  

出棧 6     
| 2 |   min = 2
| 3 |  
| 5 |     
|_3_|    
stack  

出棧 2     
| 2 |   min = 2
| 3 |  
| 5 |     
|_3_|    
stack

出棧時,當前top元素,和當前min值相同時,說明當前top元素,在入棧時是更新了最小值的元素,也就是說它的下一個元素,是之前舊的min值

所以,出棧2,出棧3,同時更新min=3

作者:windliang
連結:https://leetcode-cn.com/problems/min-stack/solution/xiang-xi-tong-su-de-si-lu-fen-xi-duo-jie-fa-by-38/
來源:力扣(LeetCode)
著作權歸作者所有。商業轉載請聯絡作者獲得授權,非商業轉載請註明出處。

python程式碼

class MinStack:

    def __init__(self):
        self.stack = []
        self.min = -1

    def push(self, val: int) -> None:
        if not self.stack:
            self.stack.append(val)
            self.min = val
        else:
            # 這裡必須用<=,必須也要包含等於
            # 比如依次push 0,1,0,push第3個0時,它等於最小值0,
            # 也需要先插入一個最小值到棧中
            # 如果不這麼做,pop 0的時候,0等於最小值,會認為前面的1是之前的最小值
            # 但1只是正常push入棧的元素
            if val <= self.min:
                self.stack.append(self.min)
                self.min = val
            self.stack.append(val)

    def pop(self) -> None:
        top = self.stack.pop()
        # 不要忘記pop後空棧的邊界情況 self.stack
        if self.stack and top==self.min:
            self.min=self.stack.pop()
        return top

    def top(self) -> int:
        return self.stack[-1]

    def getMin(self) -> int:
        return self.min

方法二:
原理和上面一樣,只是操作上略有不同。
push的時候,不是直接存入值,而是存入值和當前最小值的差。

push時:

  • 入棧值 > 最小值,push(入棧值-最小值),最小值不變
  • 入棧值 < 最小值,push(入棧值-舊最小值),新最小值變為入棧值

pop時:

  • 棧頂值 > 0,入棧值 = 棧頂值+最小值,最小值不變
  • 棧頂值 < 0,入棧值 = 最小值,之前的最小值=入棧值-棧頂值=最小值-棧頂值
入棧 3,存入 3 - 3 = 0
|   |   min = 3
|   |     
|_0_|    
stack   

入棧 5,存入 5 - 3 = 2
|   |   min = 3
| 2 |     
|_0_|    
stack  

入棧 2,因為出現了更小的數,所以我們會存入一個負數,這裡很關鍵
也就是存入  2 - 3 = -1, 並且更新 min = 2 
對於之前的 min 值 3, 我們只需要用更新後的 min - 棧頂元素 -1 就可以得到    
| -1|   min = 2
| 5 |     
|_3_|    
stack  

入棧 6,存入  6 - 2 = 4
| 4 |   min = 2
| -1| 
| 5 |     
|_3_|    
stack  

出棧,返回的值就是棧頂元素 4 加上 min,就是 6
|   |   min = 2
| -1| 
| 5 |     
|_3_|    
stack  

出棧,此時棧頂元素是負數,說明之前對 min 值進行了更新。
入棧元素 - min = 棧頂元素,入棧元素其實就是當前的 min 值 2
所以更新前的 min 就等於入棧元素 2 - 棧頂元素(-1) = 3
|   | min = 3
| 5 |     
|_3_|    
stack   

作者:windliang
連結:https://leetcode-cn.com/problems/min-stack/solution/xiang-xi-tong-su-de-si-lu-fen-xi-duo-jie-fa-by-38/
來源:力扣(LeetCode)
著作權歸作者所有。商業轉載請聯絡作者獲得授權,非商業轉載請註明出處。  

python程式碼

class MinStack:

    def __init__(self):
        self.stack = []
        self.min = -1

    def push(self, val: int) -> None:
        if not self.stack :
            self.min = val
        diff = val - self.min
        self.stack.append(diff)
        if diff < 0:
            self.min = val

    def pop(self) -> None:
        diff = self.stack.pop()
        if diff < 0:
            top = self.min
            old_min = top - diff
            self.min = old_min
        else:
            top = self.min + diff
        
        return top

    def top(self) -> int:
        diff = self.stack[-1]
        top = (diff + self.min) if diff>0 else self.min
        return top

    def getMin(self) -> int:
        return self.min

相關文章