Hard

题目描述

设计一个类似栈的数据结构,将元素推入栈中,从栈中弹出出现频率最高的元素。

实现 FreqStack 类:

  • FreqStack() 构造一个空的频率栈。
  • void push(int val) 将一个整数 val 压入栈顶。
  • int pop() 删除并返回栈中出现频率最高的元素。
    • 如果出现频率最高的元素不只一个,则移除并返回最接近栈顶的元素。

示例 1:

输入:
["FreqStack", "push", "push", "push", "push", "push", "push", "pop", "pop", "pop", "pop"]
[[], [5], [7], [5], [7], [4], [5], [], [], [], []]
输出:
[null, null, null, null, null, null, null, 5, 7, 5, 4]

解释:
FreqStack freqStack = new FreqStack();
freqStack.push(5); // 栈为 [5]
freqStack.push(7); // 栈为 [5,7]
freqStack.push(5); // 栈为 [5,7,5]
freqStack.push(7); // 栈为 [5,7,5,7]
freqStack.push(4); // 栈为 [5,7,5,7,4]
freqStack.push(5); // 栈为 [5,7,5,7,4,5]
freqStack.pop();   // 返回 5 ,因为 5 出现频率最高。栈变成 [5,7,5,7,4]。
freqStack.pop();   // 返回 7 ,因为 5 和 7 出现频率最高,但7最接近栈顶。栈变成 [5,7,5,4]。
freqStack.pop();   // 返回 5 ,因为 5 出现频率最高。栈变成 [5,7,4]。
freqStack.pop();   // 返回 4 ,因为 4, 5 和 7 出现频率最高,但 4 最接近栈顶。栈变成 [5,7]。

提示:

  • 0 <= val <= 10^9
  • pushpop 的操作数不大于 2 * 10^4
  • 输入保证在调用 pop 之前栈中至少有一个元素。

解题思路

解题思路

这道题需要设计一个频率栈,关键在于如何高效地维护频率信息和栈的性质。

核心思路

我们需要同时维护两个信息:

  1. 每个元素的出现频率
  2. 相同频率下元素的入栈顺序

关键观察:我们可以将问题转化为按频率分层的栈结构。每个频率层维护一个独立的栈。

数据结构设计

  1. freq 哈希表:记录每个元素的当前频率
  2. stacks 哈希表:以频率为键,栈为值,存储该频率下的所有元素
  3. maxFreq 变量:记录当前最大频率

操作流程

Push操作

  • 增加元素的频率计数
  • 将元素推入对应频率的栈中
  • 更新最大频率

Pop操作

  • 从最大频率的栈顶弹出元素
  • 减少该元素的频率计数
  • 如果最大频率的栈为空,减少maxFreq

这种方法巧妙地利用了栈的LIFO特性来处理"最接近栈顶"的要求,同时通过频率分层确保总是弹出频率最高的元素。

时间复杂度为O(1),空间复杂度为O(N),是最优解法。

代码实现

class FreqStack {
public:
    unordered_map<int, int> freq;
    unordered_map<int, stack<int>> stacks;
    int maxFreq;
    
    FreqStack() {
        maxFreq = 0;
    }
    
    void push(int val) {
        freq[val]++;
        stacks[freq[val]].push(val);
        maxFreq = max(maxFreq, freq[val]);
    }
    
    int pop() {
        int val = stacks[maxFreq].top();
        stacks[maxFreq].pop();
        freq[val]--;
        
        if (stacks[maxFreq].empty()) {
            maxFreq--;
        }
        
        return val;
    }
};
class FreqStack:

    def __init__(self):
        self.freq = {}
        self.stacks = {}
        self.max_freq = 0

    def push(self, val: int) -> None:
        self.freq[val] = self.freq.get(val, 0) + 1
        f = self.freq[val]
        
        if f not in self.stacks:
            self.stacks[f] = []
        self.stacks[f].append(val)
        
        self.max_freq = max(self.max_freq, f)

    def pop(self) -> int:
        val = self.stacks[self.max_freq].pop()
        self.freq[val] -= 1
        
        if not self.stacks[self.max_freq]:
            self.max_freq -= 1
            
        return val
public class FreqStack {
    private Dictionary<int, int> freq;
    private Dictionary<int, Stack<int>> stacks;
    private int maxFreq;

    public FreqStack() {
        freq = new Dictionary<int, int>();
        stacks = new Dictionary<int, Stack<int>>();
        maxFreq = 0;
    }
    
    public void Push(int val) {
        if (!freq.ContainsKey(val)) {
            freq[val] = 0;
        }
        freq[val]++;
        
        int f = freq[val];
        if (!stacks.ContainsKey(f)) {
            stacks[f] = new Stack<int>();
        }
        stacks[f].Push(val);
        
        maxFreq = Math.Max(maxFreq, f);
    }
    
    public int Pop() {
        int val = stacks[maxFreq].Pop();
        freq[val]--;
        
        if (stacks[maxFreq].Count == 0) {
            maxFreq--;
        }
        
        return val;
    }
}
var FreqStack = function() {
    this.freq = new Map();
    this.group = new Map();
    this.maxfreq = 0;
};

FreqStack.prototype.push = function(val) {
    let f = this.freq.get(val) || 0;
    f++;
    this.freq.set(val, f);
    this.maxfreq = Math.max(this.maxfreq, f);
    
    if (!this.group.has(f)) {
        this.group.set(f, []);
    }
    this.group.get(f).push(val);
};

FreqStack.prototype.pop = function() {
    let x = this.group.get(this.maxfreq).pop();
    this.freq.set(x, this.freq.get(x) - 1);
    
    if (this.group.get(this.maxfreq).length === 0) {
        this.maxfreq--;
    }
    
    return x;
};

复杂度分析

操作时间复杂度空间复杂度
PushO(1)O(N)
PopO(1)O(N)

其中 N 是栈中元素的总数。空间复杂度主要由频率哈希表和各频率层的栈所占用。