Design a max stack that supports push, pop, top, peekMax and popMax.
- push(x) -- Push element x onto stack.
- pop() -- Remove the element on top of the stack and return it.
- top() -- Get the element on the top.
- peekMax() -- Retrieve the maximum element in the stack.
- 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:
- -1e7 <= x <= 1e7
- Number of operations won't exceed 10000.
- 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; };
類似題目:
參考資料:
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