[LeetCode] Max Stack 最大棧


 

Design a max stack that supports push, pop, top, peekMax and popMax.

 

  1. push(x) -- Push element x onto stack.
  2. pop() -- Remove the element on top of the stack and return it.
  3. top() -- Get the element on the top.
  4. peekMax() -- Retrieve the maximum element in the stack.
  5. popMax() -- Retrieve the maximum element in the stack, and remove it. If you find more than one maximum elements, only remove the top-most one.

 

Example 1:

MaxStack stack = new MaxStack();
stack.push(5); 
stack.push(1);
stack.push(5);
stack.top(); -> 5
stack.popMax(); -> 5
stack.top(); -> 1
stack.peekMax(); -> 5
stack.pop(); -> 1
stack.top(); -> 5

 

Note:

  1. -1e7 <= x <= 1e7
  2. Number of operations won't exceed 10000.
  3. The last four operations won't be called when stack is empty.

 

這道題讓我們實現一個最大棧,包含一般棧的功能,但是還新加了兩個功能peekMax()和popMax(),隨時隨地可以查看和返回最大值。之前有一道很類似的題Min Stack,所以我們可以借鑒那道題的解法,使用兩個棧來模擬,s1為普通的棧,用來保存所有的數字,而s2為最大棧,用來保存出現的最大的數字。

在push()函數中,我們先來看s2,如果s2為空,或者s2的棧頂元素小於等於x,將x壓入s2中。因為s2保存的是目前為止最大的數字,所以一旦新數字大於等於棧頂元素,說明遇到更大的數字了,壓入棧。然后將數組壓入s1,s1保存所有的數字,所以都得壓入棧。

在pop()函數中,當s2的棧頂元素和s1的棧頂元素相同時,我們要移除s2的棧頂元素,因為一個數字不在s1中了,就不能在s2中。然后取出s1的棧頂元素,並移除s1,返回即可。

在top()函數中,直接返回s1的top()函數即可。

在peekMax()函數中,直接返回s2的top()函數即可。

在popMax()函數中,先將s2的棧頂元素保存到一個變量mx中,然后我們要在s1中刪除這個元素,由於棧無法直接定位元素,所以我們用一個臨時棧t,將s1的出棧元素保存到臨時棧t中,當s1的棧頂元素和s2的棧頂元素相同時退出while循環,此時我們在s1中找到了s2的棧頂元素,分別將s1和s2的棧頂元素移除,然后要做的是將臨時棧t中的元素加回s1中,注意此時容易犯的一個錯誤是,沒有同時更新s2,所以我們直接調用push()函數即可,參見代碼如下:

 

解法一:

class MaxStack {
public:
    /** initialize your data structure here. */
    MaxStack() {}
    
    void push(int x) {
        if (s2.empty() || s2.top() <= x) s2.push(x);
        s1.push(x);
    }
    
    int pop() {
        if (!s2.empty() && s2.top() == s1.top()) s2.pop();
        int t = s1.top(); s1.pop();
        return t;
    }
    
    int top() {
        return s1.top();
    }
    
    int peekMax() {
        return s2.top();
    }
    
    int popMax() {
        int mx = s2.top();
        stack<int> t;
        while (s1.top() != s2.top()) {
            t.push(s1.top()); s1.pop();
        }
        s1.pop(); s2.pop();
        while (!t.empty()) {
            push(t.top()); t.pop();
        }
        return mx;
    }

private:
    stack<int> s1, s2;
};

 

下面這種解法沒有利用一般的stack,而是建立一種較為復雜的數據結構,首先用一個list鏈表來保存所有的數字,然后建立一個數字和包含所有相同的數字的位置iterator的向量容器的映射map。

在push()函數中,把新數字加到list表頭,然后把數字x的位置iterator加到數字映射的向量容器的末尾。

在pop()函數中,先得到表頭數字,然后把該數字對應的iterator向量容器的末尾元素刪掉,如果此時向量容器為空了,將這個映射直接刪除,移除表頭數字,返回該數字即可。

在top()函數中,直接返回表頭數字即可。

在peekMax()函數中,因為map是按key值自動排序的,直接尾映射的key值即可。

在popMax()函數中,首先保存尾映射的key值,也就是最大值到變量x中,然后在其對應的向量容器的末尾取出其在list中的iterator。然后刪除該向量容器的尾元素,如果此時向量容器為空了,將這個映射直接刪除。根據之前取出的iterator,在list中刪除對應的數字,返回x即可,參見代碼如下:

 

解法二:

class MaxStack {
public:
    /** initialize your data structure here. */
    MaxStack() {}
    
    void push(int x) {
        v.insert(v.begin(), x);
        m[x].push_back(v.begin());
    }
    
    int pop() {
        int x = *v.begin();
        m[x].pop_back();
        if (m[x].empty()) m.erase(x);
        v.erase(v.begin());
        return x;
    }
    
    int top() {
        return *v.begin();
    }
    
    int peekMax() {
        return m.rbegin()->first;
    }
    
    int popMax() {
        int x = m.rbegin()->first;
        auto it = m[x].back();
        m[x].pop_back();
        if (m[x].empty()) m.erase(x);
        v.erase(it);
        return x;
    }

private:
    list<int> v;
    map<int, vector<list<int>::iterator>> m;
};

 

類似題目:

Min Stack

 

參考資料:

https://discuss.leetcode.com/topic/110014/c-using-two-stack

https://discuss.leetcode.com/topic/110066/c-o-logn-for-write-ops-o-1-for-reads 

 

LeetCode All in One 題目講解匯總(持續更新中...)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM