哈希表题目:最大频率栈

题目

标题和出处

标题:最大频率栈

出处:895. 最大频率栈

难度

7 级

题目描述

要求

设计一个类似栈的数据结构,该数据结构支持将元素入栈和将最大频率的元素出栈。

实现 FreqStack \texttt{FreqStack} FreqStack 类:

  • FreqStack() \texttt{FreqStack()} FreqStack() 创建空的频率栈。
  • void   push(int   val) \texttt{void push(int val)} void push(int val) 将整数 val \texttt{val} val 入栈。
  • int   pop() \texttt{int pop()} int pop() 移除并返回栈内最大频率的元素。
    • 如果有多个最大频率的元素,则移除并返回最接近栈顶的元素。

示例

示例 1:

输入:
["FreqStack",   "push",   "push",   "push",   "push",   "push",   "push",   "pop",   "pop",   "pop",   "pop"] \texttt{["FreqStack", "push", "push", "push", "push", "push", "push", "pop", "pop", "pop", "pop"]} ["FreqStack", "push", "push", "push", "push", "push", "push", "pop", "pop", "pop", "pop"]
[[],   [5],   [7],   [5],   [7],   [4],   [5],   [],   [],   [],   []] \texttt{[[], [5], [7], [5], [7], [4], [5], [], [], [], []]} [[], [5], [7], [5], [7], [4], [5], [], [], [], []]
输出:
[null,   null,   null,   null,   null,   null,   null,   5,   7,   5,   4] \texttt{[null, null, null, null, null, null, null, 5, 7, 5, 4]} [null, null, null, null, null, null, null, 5, 7, 5, 4]
解释:
FreqStack   freqStack   =   new   FreqStack(); \texttt{FreqStack freqStack = new FreqStack();} FreqStack freqStack = new FreqStack();
freqStack.push(5); \texttt{freqStack.push(5);} freqStack.push(5); // 栈为 [5] \texttt{[5]} [5]
freqStack.push(7); \texttt{freqStack.push(7);} freqStack.push(7); // 栈为 [5,7] \texttt{[5,7]} [5,7]
freqStack.push(5); \texttt{freqStack.push(5);} freqStack.push(5); // 栈为 [5,7,5] \texttt{[5,7,5]} [5,7,5]
freqStack.push(7); \texttt{freqStack.push(7);} freqStack.push(7); // 栈为 [5,7,5,7] \texttt{[5,7,5,7]} [5,7,5,7]
freqStack.push(4); \texttt{freqStack.push(4);} freqStack.push(4); // 栈为 [5,7,5,7,4] \texttt{[5,7,5,7,4]} [5,7,5,7,4]
freqStack.push(5); \texttt{freqStack.push(5);} freqStack.push(5); // 栈为 [5,7,5,7,4,5] \texttt{[5,7,5,7,4,5]} [5,7,5,7,4,5]
freqStack.pop(); \texttt{freqStack.pop();} freqStack.pop(); // 返回 5 \texttt{5} 5,因为 5 \texttt{5} 5 是最大频率的元素。栈变成 [5,7,5,7,4] \texttt{[5,7,5,7,4]} [5,7,5,7,4]
freqStack.pop(); \texttt{freqStack.pop();} freqStack.pop(); // 返回 7 \texttt{7} 7,因为 5 \texttt{5} 5 7 \texttt{7} 7 是最大频率的元素,但是 7 \texttt{7} 7 最接近栈顶。栈变成 [5,7,5,4] \texttt{[5,7,5,4]} [5,7,5,4]
freqStack.pop(); \texttt{freqStack.pop();} freqStack.pop(); // 返回 5 \texttt{5} 5,因为 5 \texttt{5} 5 是最大频率的元素。栈变成 [5,7,4] \texttt{[5,7,4]} [5,7,4]
freqStack.pop(); \texttt{freqStack.pop();} freqStack.pop(); // 返回 4 \texttt{4} 4,因为 4 \texttt{4} 4 5 \texttt{5} 5 7 \texttt{7} 7 是最大频率的元素,但是 4 \texttt{4} 4 最接近栈顶。栈变成 [5,7] \texttt{[5,7]} [5,7]

数据范围

  • 0 ≤ val ≤ 10 9 \texttt{0} \le \texttt{val} \le \texttt{10}^\texttt{9} 0val109
  • 最多调用 2 × 10 4 \texttt{2} \times \texttt{10}^\texttt{4} 2×104 push \texttt{push} push pop \texttt{pop} pop
  • 题目保证调用 pop \texttt{pop} pop 时,栈内至少有一个元素

解法

思路和算法

为了实现移除栈内最大频率的元素,需要记录栈内每个元素的频率,以及最大频率。当有多个最大频率的元素时,为了实现移除最接近栈顶的元素,需要使用栈存储最大频率对应的全部元素,从栈内移除的元素一定是最接近栈顶的元素。

最大频率栈的数据结构中需要维护以下信息:

  • 元素频率哈希表,记录每个元素的频率;
  • 频率栈哈希表,记录每个频率对应的元素;
  • 最大频率,记录最大频率栈内的所有元素的最大频率。

构造方法中,将两个哈希表初始化,将最大频率初始化为 0 0 0

对于 push \textit{push} push 操作,做法如下:

  1. 在元素频率哈希表中将 val \textit{val} val 的频率加 1 1 1
  2. val \textit{val} val 的更新后的频率记为 freq \textit{freq} freq,在频率栈哈希表中得到 freq \textit{freq} freq 对应的栈,将 val \textit{val} val 入栈;
  3. 使用 freq \textit{freq} freq 更新最大频率。

对于 pop \textit{pop} pop 操作,做法如下:

  1. 在频率栈哈希表中得到最大频率对应的栈,将栈顶元素出栈,记为 val \textit{val} val
  2. 在元素频率哈希表中将 val \textit{val} val 的频率减 1 1 1,如果 val \textit{val} val 的频率变成 0 0 0 则将 val \textit{val} val 从元素频率哈希表中移除;
  3. 如果最大频率对应的栈变为空,则将最大频率减 1 1 1
  4. 返回 val \textit{val} val

上述做法的正确性说明如下。

  1. 元素频率哈希表记录每个元素的频率,因此可以通过元素频率哈希表得到每个元素的频率。
  2. 频率栈哈希表中,同一个元素值会多次出现。如果元素 val \textit{val} val 的频率是 freq \textit{freq} freq,则 val \textit{val} val 会出现在从 1 1 1 freq \textit{freq} freq 的每个频率对应的栈内,即 val \textit{val} val 会出现在 freq \textit{freq} freq 个不同频率的栈内。由于每个频率对应一个栈,栈的特点是后进先出,因此在出栈操作时,最大频率的元素中的最接近栈顶的元素最先被移除,符合题目的要求。
  3. maxFreq \textit{maxFreq} maxFreq 表示最大频率,将最大频率对应的栈的栈顶元素移除之后,被移除的元素的频率变成 maxFreq − 1 \textit{maxFreq} - 1 maxFreq1,因此最大频率栈内的其余元素的最大频率至少为 maxFreq − 1 \textit{maxFreq} - 1 maxFreq1。移除最大频率的元素之后,可能有两种情况:
    • 如果只有一个最大频率元素,则该元素被移除之后, maxFreq \textit{maxFreq} maxFreq 对应的栈为空,此时最大频率变成 maxFreq − 1 \textit{maxFreq} - 1 maxFreq1
    • 如果有多个最大频率元素,则该元素被移除之后, maxFreq \textit{maxFreq} maxFreq 对应的栈不为空,最大频率仍为 maxFreq \textit{maxFreq} maxFreq

代码

class FreqStack {
    Map<Integer, Integer> numFreqMap;
    Map<Integer, Deque<Integer>> freqStackMap;
    int maxFreq;

    public FreqStack() {
        numFreqMap = new HashMap<Integer, Integer>();
        freqStackMap = new HashMap<Integer, Deque<Integer>>();
        maxFreq = 0;
    }
    
    public void push(int val) {
        int freq = numFreqMap.getOrDefault(val, 0) + 1;
        numFreqMap.put(val, freq);
        freqStackMap.putIfAbsent(freq, new ArrayDeque<Integer>());
        freqStackMap.get(freq).push(val);
        maxFreq = Math.max(maxFreq, freq);
    }
    
    public int pop() {
        Deque<Integer> stack = freqStackMap.get(maxFreq);
        int val = stack.pop();
        numFreqMap.put(val, numFreqMap.get(val) - 1);
        if (numFreqMap.get(val) == 0) {
            numFreqMap.remove(val);
        }
        if (stack.isEmpty()) {
            maxFreq--;
        }
        return val;
    }
}

复杂度分析

  • 时间复杂度:构造方法和各项操作的时间复杂度都是 O ( 1 ) O(1) O(1)。构造方法初始化两个哈希表和最大频率,各项操作均为哈希表操作和栈操作,因此时间复杂度是 O ( 1 ) O(1) O(1)

  • 空间复杂度: O ( n ) O(n) O(n),其中 n n n 是最大频率栈的元素个数。需要使用两个哈希表分别记录每个元素值的频率以及每个频率对应的元素,每个哈希表中的元素个数都不会超过最大频率栈的元素个数。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

伟大的车尔尼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值