难度:hard
说明:
要求设计一个统计频次的栈,push() 往栈内存放元素,pop() 返回栈内频次最多的元素,并移除该元素,如果频次一样,就返回最靠近栈顶的一个。
题目连接:https://leetcode.com/problems/maximum-frequency-stack/submissions/
输入范围:
FreqStack.push(int x)
will be such that 0 <= x <= 10^9
.FreqStack.pop()
won't be called if the stack has zero elements.FreqStack.push
calls will not exceed 10000
in a single test case.FreqStack.pop
calls will not exceed 10000
in a single test case.FreqStack.push
and FreqStack.pop
calls will not exceed 150000
across all test cases.输入案例:
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].
思路上跟 LRU 差不多的,频次优先,栈顶优先,只是少了插入优先,其实可用优先队列来做 PriorityQueue 。
1、使用 stack 存放 每次插入的元素。
2、使用 cache 记录每个元素值出现的 频次。
3、使用 优先队列,存放优先的频次值。
4、使用 优先队列 HashMap indexs,记录每个频次对应的优先 stack 索引。
具体是这样的指针指向(图后面补上):
但是可以更加简单化:
1、因为每次存放进来的时候,stack 索引只增不减,所以 indexs 不必使用优先队列,可以改为 普通 ArrayList 即可。
2、如果改下存放思路:
使用 cache 记录每个元素出现频次。
然后使用频次 - 数值组 来存放数值: List>,那么 根据 频次 来分组,每个组内都存放一个 数值的 list,再来一个 maxFre 记录最大的频次,每次获取组都获取maxFre 的对应组,并且获取组最后一个元素,那么这个元素就是最大的并且最靠近栈顶的频次数值。
Java:
class FreqStack {
int maxFre = 0; // 用于记录频次, 降低创建 ArrayList 次数
Map cache; // 暂存频率节点
private static List> freValsStack = new ArrayList<>();; // 暂存 频次 - 数据组
public FreqStack() {
cache = new HashMap<>();
}
public void push(int x) {
Integer fre = cache.getOrDefault(x, 0) + 1;
if(fre > freValsStack.size()) {
freValsStack.add(new ArrayList<>());
}
freValsStack.get(fre - 1).add(x); // 每个频次组最后一个元素必定是最新插入的
cache.put(x, fre);
maxFre = Math.max(maxFre, fre);
}
public int pop() {
if(freValsStack.isEmpty()) return -1;
// 移除频次最多
List topList = freValsStack.get(maxFre - 1);
Integer res = topList.remove(topList.size() - 1);
// 频次 - 1
if(topList.isEmpty()) maxFre --;
cache.put(res, cache.get(res) - 1);
return res;
}
}
C++(后面补上):
使用优先队列的 Java:
class FreqStack {
int top;
List stack; // 暂存数据
Map cache; // 暂存频率节点
PriorityQueue queue; // 频率优先
Map> indexs; // 索引优先
public FreqStack2() {
queue = new PriorityQueue<>((i1, i2) -> i2 - i1);
indexs = new HashMap<>();
cache = new HashMap<>();
stack = new ArrayList<>();
}
public void push(int x) {
stack.add(x);
Integer fre;
// 获取该数值频率
fre = cache.getOrDefault(x, 0) + 1;
cache.put(x, fre);
if(indexs.containsKey(fre)) { // 增加该频率优先索引
Queue q = indexs.get(fre);
q.offer(top ++);
} else {
Queue q = new PriorityQueue<>((t1, t2) -> t2 - t1);
q.offer(top ++);
indexs.put(fre, q);
}
// 添加优先频率
queue.offer(fre);
}
public int pop() {
if(queue.isEmpty()) return -1;
Integer fre = queue.poll();
Integer index = indexs.get(fre).poll();
Integer res = stack.get(index);
cache.put(res, cache.get(res) - 1);
return res;
}
}