[每日一題] 第二十七題:包含min函式的棧

DRose發表於2020-08-10

定義棧的資料結構,請在該型別中實現一個能夠得到棧的最小元素的 min 函式在該棧中,呼叫 min、push 及 pop 的時間複雜度都是 O(1)。

示例:

MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.min();   --> 返回 -3.
minStack.pop();
minStack.top();      --> 返回 0.
minStack.min();   --> 返回 -2.

提示:

  1. 各函式的呼叫總次數不超過 20000 次

方法一:輔助棧

解題思路

普通棧的 push()pop() 函式的複雜度為 O(1),而獲取棧最小值 min() 函式需要遍歷整個棧,複雜度為 O(N)。

  • 本題難點:將 min() 函式複雜度降為 O(1),可通過建立輔助棧實現;

    • 資料棧 A:棧 A 用於儲存所有元素,保證入棧 push() 函式、出棧 pop() 函式、獲取棧頂 top() 函式的正常邏輯;
    • 輔助棧 B:棧 B 中儲存棧 A 中所有 非嚴格降序 的元素,則棧 A 中的最小元素始終對應棧 B 的棧頂元素,即 min() 函式只需返回棧 B 的棧頂元素即可。
  • 因此,只需設法維護好棧 B 的元素,使其保持非嚴格降序,即可實現 min() 函式的 O(1) 複雜度。

[每日一題] 第二十七題:包含min函式的棧

函式設計

  • push(x) 函式:重點為保持棧 B 的元素是 非嚴格降序 的。

    1. 將 x 壓入棧 A(即 A.add(x));
    2. 若 ① 棧 B 為空 或 ② x 小於等於 棧 B 的棧頂元素,則將 x 壓入棧 B(即 B.add(x))。
  • pop() 函式:重點為保持棧 A B 的 元素一致性

    1. 執行棧 A 出棧(即 A.pop()),將出棧元素記為 y;
    2. 若 y 等於棧 B 的棧頂元素,則執行棧 B 出棧(即 B.pop())。
  • top() 函式:直接返回棧 A 的棧頂元素即可,即返回 A.peek()

  • min() 函式:直接返回棧 B 的棧頂元素即可,即返回 B.peek()

程式碼

Java 程式碼中,由於 Stack 中儲存的是 int 的包裝類 Integer,因此需要使用 equals() 代替 == 來比較值是否相等。

class MinStack {
    Stack<Integer> A, B;
    public MinStack() {
        A = new Stack<>();
        B = new Stack<>();
    }
    public void push(int x) {
        A.add(x);
        if(B.empty() || B.peek() >= x)
            B.add(x);
    }
    public void pop() {
        if(A.pop().equals(B.peek()))
            B.pop();
    }
    public int top() {
        return A.peek();
    }
    public int min() {
        return B.peek();
    }
}

複雜度分析

  • 時間複雜度 O(1)push()pop()top()min() 四個函式的時間複雜度均為常數級別。
  • 空間複雜度 O(N) :當共有 N 個待入棧元素時,輔助棧 B 最差情況下儲存 N 個元素,使用 O(N) 額外空間。

個人理解

  1. 比較兩個值相等一定要用 equals() 方法,因為 Stack 中存的是 Integer,不能用 == 來判斷相等。
  2. 元素一致性:上面程式碼中已經有了處理元素一致性的方法,就是在 A 棧中 pop 出來的元素,如果和 B 棧的棧頂元素相等,則將 B 棧的棧頂元素也 pop 出來。我們來看一下為什麼這麼做可以保證元素一致性。
    1. B 棧是非嚴格遞減的,也就是說,在向 A 棧內新增元素的時候,如果後面的一個元素大於前面一個元素,是不會將元素壓入 B 棧中的。
    2. 這就避免了我在 A 棧中彈出一個元素,結果在 B 棧中存在,但是不在棧頂,導致最後 B 棧元素不空等各種問題。因為我根本不會將大值壓入 B 棧中。所以只需要比較棧頂元素即可,非嚴格遞減儲存的。
    3. 為什麼這樣可以,假設壓入 A 棧中第一個元素是最小的,那麼 B 棧只會有一個元素,整個過程中最小元素都是第一個,直到 A 中 pop 出最後一個元素。

方法二:我的解法

我是借鑑了上面的解法,不過我使用的是 Deque,因為 Stack 已經不推薦使用了。

class MinStack {

    Deque<Integer> A,B;

    /** initialize your data structure here. */
    public MinStack() {
        A = new LinkedList<>();
        B = new LinkedList<>();
    }

    public void push(int x) {
        A.addFirst(x);
        if (B.isEmpty() ||  x <= B.peekFirst()) {
            B.addFirst(x);
        }
    }

    public void pop() {
        if (A.removeFirst().equals(B.peekFirst())) {
            B.removeFirst();
        }
    }

    public int top() {
        return A.peekFirst();
    }

    public int min() {
        return B.peekFirst();
    }
}

/**
 * Your MinStack object will be instantiated and called as such:
 * MinStack obj = new MinStack();
 * obj.push(x);
 * obj.pop();
 * int param_3 = obj.top();
 * int param_4 = obj.min();
 */

題解來源

作者:jyd
連結:leetcode-cn.com/problems/bao-han-m...
來源:力扣(LeetCode)

來源:力扣(LeetCode)
連結:leetcode-cn.com/problems/bao-han-m...

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章