LeetCode-895. 最大频率栈【栈,哈希表,设计,有序集合】

这篇博客讨论了如何设计一个最大频率栈,该数据结构支持push和pop操作,并在pop时返回当前频率最高的元素。解题策略包括使用哈希表和栈来存储元素及其频率,以及优化后的解决方案,通过数组下标代替维护最大频率。每个解法的时间复杂度均为O(1),空间复杂度为O(n)。
摘要由CSDN通过智能技术生成

题目描述:

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

实现 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 = 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 <= 109
push 和 pop 的操作数不大于 2 * 104。
输入保证在调用 pop 之前堆栈中至少有一个元素。
https://leetcode.cn/problems/maximum-frequency-stack/

解题思路一:哈希表和栈。这道题奇妙之处在于把出现频率相同的值存储为一组元素,这样在pop的时候,找到频率最高的那组元素,按照后入先出的顺序,弹出即可。

还是以题目给的数据为例:

freqStack.push (5);//堆栈为 [5]
freqStack.push (7);//堆栈是 [5,7]//group为{1{5,7}}
freqStack.push (5);//堆栈是 [5,7,5]
freqStack.push (7);//堆栈是 [5,7,5,7]//group为{1{5,7}} {2{5,7}}
freqStack.push (4);//堆栈是 [5,7,5,7,4]//group为{1{5,7,4}} {2{5,7}}
freqStack.push (5);//堆栈是 [5,7,5,7,4,5]//group为{1{5,7,4}} {2{5,7}} {3{5}}}
然后最大频率为3,那么pop就会从freq 为3的那组值中选择最后加入的值弹出, 当前应该弹出值5{1{5,7,4}} {2{5,7}} {3{ }}}

从group最后的状态可以看出{1,{5,7,4}} {2,{5,7}} {3,{5}}}。
只要依次由maxFreq的栈中出栈元素即可。
即从右往左数,5,7,5,4即是出栈顺序。

class FreqStack {
public:
    int maxFreq;//遍历group用
    unordered_map<int,int> freq;//值和对应的频率,更新group用
    unordered_map<int,stack<int>> group;//需要注意的是group只是增加和遍历,没有覆盖和删除操作。
    FreqStack() {maxFreq=0;}//初始化   
    void push(int val) {
        ++freq[val];
        maxFreq=max(maxFreq,freq[val]);
        group[freq[val]].push(val);//val的频率增加了1,也就是说频率为freq_map[val]的值加一,对应更新group。
    }    
    int pop() {
        int x=group[maxFreq].top();group[maxFreq].pop();//获取栈顶元素和出栈
        --freq[x];//pop和push操作有可能是交替进行的,所以维护更新。
        if(group[maxFreq].empty()) --maxFreq;
        return x;        
    }
};
/**
 * Your FreqStack object will be instantiated and called as such:
 * FreqStack* obj = new FreqStack();
 * obj->push(val);
 * int param_2 = obj->pop();
 */

时间复杂度:O(1)对于 push 和 pop操作,时间复杂度为 O(1)。
空间复杂度:O(n)//n为FreqStack中的元素个数。

解题思路二:无注释版。

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

/**
 * Your FreqStack object will be instantiated and called as such:
 * FreqStack* obj = new FreqStack();
 * obj->push(val);
 * int param_2 = obj->pop();
 */

时间复杂度:O(1)对于 push 和 pop操作,时间复杂度为 O(1)。
空间复杂度:O(n)//n为FreqStack中的元素个数。

解题思路三:优化!这里我们直接省去维护最大值的操作以数组下标代替。

class FreqStack {
    unordered_map<int, int> cnt;
    vector<stack<int>> stacks;
public:
    void push(int val) {
        if (cnt[val] == stacks.size()) // 这个元素的频率已经是目前最多的,现在又出现了一次
            stacks.push_back({}); // 那么必须创建一个新栈
        stacks[cnt[val]].push(val);
        ++cnt[val]; // 更新频率
    }

    int pop() {
        int val = stacks.back().top(); // 弹出最右侧栈的栈顶
        stacks.back().pop();
        if (stacks.back().empty()) // 栈为空
            stacks.pop_back(); // 删除
        --cnt[val]; // 更新频率
        return val;
    }
};

时间复杂度:O(1)对于 push 和 pop操作,时间复杂度为 O(1)。
空间复杂度:O(n)//n为FreqStack中的元素个数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

旋转的油纸伞

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

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

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

打赏作者

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

抵扣说明:

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

余额充值