Implement `FreqStack`, a class which simulates the operation of a stack-like data structure.
FreqStack
has two functions:
push(int x)
, which pushes an integerx
onto the stack.pop()
, which removes and returns the most frequent element in the stack.- If there is a tie for most frequent element, the element closest to the top of the stack is removed and returned.
Example 1:
Input:
["FreqStack","push","push","push","push","push","push","pop","pop","pop","pop"],
[[],[5],[7],[5],[7],[4],[5],[],[],[],[]]
Output: [null,null,null,null,null,null,null,5,7,5,4]
Explanation:
After making six .push operations, the stack is [5,7,5,7,4,5] from bottom to top. Then:
pop() -> returns 5, as 5 is the most frequent.
The stack becomes [5,7,5,7,4].
pop() -> returns 7, as 5 and 7 is the most frequent, but 7 is closest to the top.
The stack becomes [5,7,5,4].
pop() -> returns 5.
The stack becomes [5,7,4].
pop() -> returns 4.
The stack becomes [5,7].
Note:
- Calls to
FreqStack.push(int x)
will be such that0 <= x <= 10^9
. - It is guaranteed that
FreqStack.pop()
won't be called if the stack has zero elements. - The total number of
FreqStack.push
calls will not exceed10000
in a single test case. - The total number of
FreqStack.pop
calls will not exceed10000
in a single test case. - The total number of
FreqStack.push
andFreqStack.pop
calls will not exceed150000
across all test cases.
這道題讓我們實現一種最大頻率棧,有入棧和出棧功能,需要每次出棧的都是棧中出現頻率最大的數字,若有多個數字的頻率相同,那么離棧頂最近的元素先出棧。剛開始看到這道題的時候,博主立馬聯想到了 [LRU Cache](http://www.cnblogs.com/grandyang/p/4587511.html) 和 [LFU Cache](http://www.cnblogs.com/grandyang/p/6258459.html),想着會不會也需要在迭代器上做文章,但實際是我想多了,雖然同為 Hard 的題目,這道題的解法卻要比之前那兩道要簡單的多。這里只跟數字出現的頻率有關,只有在頻率相等的情況下才會考慮棧的后入先出的特性,所以一定是需要統計棧中每個數字出現的頻率的,我們使用一個 HashMap 來建立每個數字跟其出現次數之間的映射。由於頻率相等的數字可能有多個,所以我們必須知道某個特性頻率下都有哪些數字,再用一個 HashMap 來建立頻率和該頻率下所有的數字之間的映射,可以將這些數組放到一個數組或者一個棧中,這里為了簡便起見,就使用一個數組了。另外,我們還需要維護一個當前最大頻率的變量,可以通過這個值到 HashMap 中快速定位數組的位置。好,一切准備就緒之后就開始解題吧,對於入棧函數 push(),首先需要將x對應的映射值加1,並更新最大頻率 mxFreq,然后就是要把x加入當前頻率對應的數組中,注意若某個數字出現了3次,那么數字會分別加入頻率為 1,2,3 的映射數組中。接下來看出棧函數 pop() 如何實現,由於我們知道當前最大頻率 mxFreq,就可以直接去 HashMap 中取出該頻率下的所有數字的數組,題目說了若頻率相等,取離棧頂最近的元素,這里就是數組末尾的數組,取到之后,要將該數字從數組末尾移除。移除之后,我們要檢測一下,若數組此時為空了,說明當前最大頻率下之后一個數字,取出之后,最大頻率就要自減1,還有不要忘記的就是取出數字的自身的頻率值也要自減1,參見代碼如下:
解法一:
class FreqStack {
public:
FreqStack() {}
void push(int x) {
mxFreq = max(mxFreq, ++freq[x]);
m[freq[x]].push_back(x);
}
int pop() {
int x = m[mxFreq].back();
m[mxFreq].pop_back();
if (m[freq[x]--].empty()) --mxFreq;
return x;
}
private:
int mxFreq;
unordered_map<int, int> freq;
unordered_map<int, vector<int>> m;
};
我們還可以使用 multimap 來建立頻率和數字之間的映射,利用其可重復的特性,那么同一個頻率就可以映射多個數字了。同時,由於 multimap 默認是按從小到大排序的,而我們希望按頻率從大到小排序,所以加上一個參數使其改變排序方式。在入棧函數中,將x的頻率自增1,然后跟x組成 pair 對兒加入 multimap 中。在出棧函數中,由於其是按從大到小排序的,而且后進的排在前面,那么第一個映射對兒就是頻率最大且最后加入的數字,將其取出並從 multimap 中移除,並同時將該數字的映射頻率值減1即可,參見代碼如下:
解法二:
class FreqStack {
public:
FreqStack() {}
void push(int x) {
m.insert({++freq[x], x});
}
int pop() {
int x = m.begin()->second;
m.erase(m.begin());
--freq[x];
return x;
}
private:
unordered_map<int, int> freq;
multimap<int, int, greater_equal<int>> m;
};
Github 同步地址:
https://github.com/grandyang/leetcode/issues/895
參考資料:
https://leetcode.com/problems/maximum-frequency-stack/
https://leetcode.com/problems/maximum-frequency-stack/discuss/163410/C%2B%2BJavaPython-O(1)
[LeetCode All in One 題目講解匯總(持續更新中...)](https://www.cnblogs.com/grandyang/p/4606334.html)