[LeetCode] 895. Maximum Frequency Stack 最大頻率棧



Implement `FreqStack`, a class which simulates the operation of a stack-like data structure.

FreqStack has two functions:

  • push(int x), which pushes an integer xonto 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 that 0 <= 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 exceed 10000 in a single test case.
  • The total number of FreqStack.pop calls will not exceed 10000 in a single test case.
  • The total number of FreqStack.push and FreqStack.pop calls will not exceed 150000across 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)

https://leetcode.com/problems/maximum-frequency-stack/discuss/229638/C%2B%2B-multimap-solution-132-ms

https://leetcode.com/problems/maximum-frequency-stack/discuss/163453/JAVA-O(1)-solution-easy-understand-using-bucket-sort


[LeetCode All in One 題目講解匯總(持續更新中...)](https://www.cnblogs.com/grandyang/p/4606334.html)


免責聲明!

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



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