问题描述
- 最大频率栈 实现 FreqStack,模拟类似栈的数据结构的操作的一个类。
FreqStack 有两个函数:
push(int x),将整数 x 推入栈中。 pop(),它移除并返回栈中出现最频繁的元素。
如果最频繁的元素不只一个,则移除并返回最接近栈顶的元素。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/maximum-frequency-stack
解题思路
这题依然主要是按照频率和按照时间排序,和之前写的LFU 算法c++的两种实现 问题上是类似的。只不过 LFU中,每个元素最多出现一次。
因此,依旧按照类似的思路,提供两种解法。
思路一【hash表+优先级队列】
- 第一种是将频率信息和时间戳信息记录到每一个元素节点里面,然后再按照频率从大到小、时间从大到小的顺序排序,每次都取前一个。
- 这里依然需要一个hash表来记录<值,频率>的映射,因为push的时候或者pop的时候都需要知道该元素是否在优先级中队列中出现过,以及它的出现的频率。因为新出现的频率总是它原来的频率+1。
- 这个和LFU算法不同,LFU算法中,get函数是带参数的,必须返回想要get(x)的那个x,因此仅仅记录频率还不够,还得知道它在哪,然后在优先级队列中进行更新频率和时间(删除再新建节点插入,优先级队列只能删除堆顶元素,因此LFU中使用set实现)。而本题中,pop返回的是堆顶元素,并且直接删除即可。push的时候是创建新节点,根本不需要管旧节点在什么地方。
struct Node{
int val;
int time;
int freq;
Node(){}
Node(int _val, int _time, int _freq){
val=_val;
time=_time;
freq=_freq;
}
};
struct cmp{
bool operator () (const Node& lhs,const Node&rhs)const {
// 默认大根堆,因此我们按频率从小到大、时间戳从小到大排序即可
return lhs.freq==rhs.freq?lhs.time<rhs.time:lhs.freq<rhs.freq;
}
};
class FreqStack {
public:
unordered_map<int,int> v2f;
priority_queue<Node,vector<Node>,cmp>pq;
int global_time=0;
FreqStack() {
}
void push(int val) {
int f;
if(v2f.count(val)){
// 如果已经有了,则新节点的频率+1
f = ++v2f[val];
}else{
f=1;
v2f[val]=1;
}
Node n(val,global_time++,f);
pq.push(n);
}
int pop() {
if(pq.empty()){
return -1;
}
Node n=pq.top();
pq.pop();
// 更新hash表
v2f[n.val]--;
if(!v2f[n.val])
{
v2f.erase(n.val);
}
return n.val;
}
};
思路二:hash表+单链表
- 使用数组下标来实现freq的有序,数组下标就是freq(默认freq=0,这样不浪费)
- 数组中的每个元素是一个单链表(也可以就直接是栈),每次从链表头插入,删除也是从直接删头。
- 使用hash表记录<val,freq>的映射
- push:hash表更新freq,生成新节点,放到对应freq的链表头部。
- pop:从数组最后一个链表头部删除,如果链表空了则把这个数组元素也删除
class FreqStack {
public:
vector<list<int>> array;
unordered_map<int,int>v2f;
FreqStack() {
array.push_back(list<int>());
}
void push(int val) {
int f;
if(v2f.count(val)){
f = ++v2f[val];
}else{
f =0;
v2f[val]=0;
}
if(f==array.size()){
array.push_back(list<int>());
}
array[f].push_front(val);
}
int pop() {
int res = array.back().front();
v2f[res]--;
if(v2f[res]==-1){
v2f.erase(res);
}
array.back().pop_front();
if(array.back().empty())
array.pop_back();
return res;
}
};