您现在的位置是:首页 >技术杂谈 >哈希表题目:最大频率栈网站首页技术杂谈
哈希表题目:最大频率栈
题目
标题和出处
标题:最大频率栈
出处:895. 最大频率栈
难度
7 级
题目描述
要求
设计一个类似栈的数据结构,该数据结构支持将元素入栈和将最大频率的元素出栈。
实现 FreqStack exttt{FreqStack} FreqStack 类:
- FreqStack() exttt{FreqStack()} FreqStack() 创建空的频率栈。
- void push(int val) exttt{void push(int val)} void push(int val) 将整数 val exttt{val} val 入栈。
-
int
pop()
exttt{int pop()}
int pop() 移除并返回栈内最大频率的元素。
- 如果有多个最大频率的元素,则移除并返回最接近栈顶的元素。
示例
示例 1:
输入:
["FreqStack",
"push",
"push",
"push",
"push",
"push",
"push",
"pop",
"pop",
"pop",
"pop"]
exttt{["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],
[],
[],
[],
[]]
exttt{[[], [5], [7], [5], [7], [4], [5], [], [], [], []]}
[[], [5], [7], [5], [7], [4], [5], [], [], [], []]
输出:
[null,
null,
null,
null,
null,
null,
null,
5,
7,
5,
4]
exttt{[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();
exttt{FreqStack freqStack = new FreqStack();}
FreqStack freqStack = new FreqStack();
freqStack.push(5);
exttt{freqStack.push(5);}
freqStack.push(5); // 栈为
[5]
exttt{[5]}
[5]
freqStack.push(7);
exttt{freqStack.push(7);}
freqStack.push(7); // 栈为
[5,7]
exttt{[5,7]}
[5,7]
freqStack.push(5);
exttt{freqStack.push(5);}
freqStack.push(5); // 栈为
[5,7,5]
exttt{[5,7,5]}
[5,7,5]
freqStack.push(7);
exttt{freqStack.push(7);}
freqStack.push(7); // 栈为
[5,7,5,7]
exttt{[5,7,5,7]}
[5,7,5,7]
freqStack.push(4);
exttt{freqStack.push(4);}
freqStack.push(4); // 栈为
[5,7,5,7,4]
exttt{[5,7,5,7,4]}
[5,7,5,7,4]
freqStack.push(5);
exttt{freqStack.push(5);}
freqStack.push(5); // 栈为
[5,7,5,7,4,5]
exttt{[5,7,5,7,4,5]}
[5,7,5,7,4,5]
freqStack.pop();
exttt{freqStack.pop();}
freqStack.pop(); // 返回
5
exttt{5}
5,因为
5
exttt{5}
5 是最大频率的元素。栈变成
[5,7,5,7,4]
exttt{[5,7,5,7,4]}
[5,7,5,7,4]。
freqStack.pop();
exttt{freqStack.pop();}
freqStack.pop(); // 返回
7
exttt{7}
7,因为
5
exttt{5}
5 和
7
exttt{7}
7 是最大频率的元素,但是
7
exttt{7}
7 最接近栈顶。栈变成
[5,7,5,4]
exttt{[5,7,5,4]}
[5,7,5,4]。
freqStack.pop();
exttt{freqStack.pop();}
freqStack.pop(); // 返回
5
exttt{5}
5,因为
5
exttt{5}
5 是最大频率的元素。栈变成
[5,7,4]
exttt{[5,7,4]}
[5,7,4]。
freqStack.pop();
exttt{freqStack.pop();}
freqStack.pop(); // 返回
4
exttt{4}
4,因为
4
exttt{4}
4、
5
exttt{5}
5 和
7
exttt{7}
7 是最大频率的元素,但是
4
exttt{4}
4 最接近栈顶。栈变成
[5,7]
exttt{[5,7]}
[5,7]。
数据范围
- 0 ≤ val ≤ 10 9 exttt{0} le exttt{val} le exttt{10}^ exttt{9} 0≤val≤109
- 最多调用 2 × 10 4 exttt{2} imes exttt{10}^ exttt{4} 2×104 次 push exttt{push} push 和 pop exttt{pop} pop
- 题目保证调用 pop exttt{pop} pop 时,栈内至少有一个元素
解法
思路和算法
为了实现移除栈内最大频率的元素,需要记录栈内每个元素的频率,以及最大频率。当有多个最大频率的元素时,为了实现移除最接近栈顶的元素,需要使用栈存储最大频率对应的全部元素,从栈内移除的元素一定是最接近栈顶的元素。
最大频率栈的数据结构中需要维护以下信息:
- 元素频率哈希表,记录每个元素的频率;
- 频率栈哈希表,记录每个频率对应的元素;
- 最大频率,记录最大频率栈内的所有元素的最大频率。
构造方法中,将两个哈希表初始化,将最大频率初始化为 0 0 0。
对于 push extit{push} push 操作,做法如下:
- 在元素频率哈希表中将 val extit{val} val 的频率加 1 1 1;
- 将 val extit{val} val 的更新后的频率记为 freq extit{freq} freq,在频率栈哈希表中得到 freq extit{freq} freq 对应的栈,将 val extit{val} val 入栈;
- 使用 freq extit{freq} freq 更新最大频率。
对于 pop extit{pop} pop 操作,做法如下:
- 在频率栈哈希表中得到最大频率对应的栈,将栈顶元素出栈,记为 val extit{val} val;
- 在元素频率哈希表中将 val extit{val} val 的频率减 1 1 1,如果 val extit{val} val 的频率变成 0 0 0 则将 val extit{val} val 从元素频率哈希表中移除;
- 如果最大频率对应的栈变为空,则将最大频率减 1 1 1;
- 返回 val extit{val} val。
上述做法的正确性说明如下。
- 元素频率哈希表记录每个元素的频率,因此可以通过元素频率哈希表得到每个元素的频率。
- 频率栈哈希表中,同一个元素值会多次出现。如果元素 val extit{val} val 的频率是 freq extit{freq} freq,则 val extit{val} val 会出现在从 1 1 1 到 freq extit{freq} freq 的每个频率对应的栈内,即 val extit{val} val 会出现在 freq extit{freq} freq 个不同频率的栈内。由于每个频率对应一个栈,栈的特点是后进先出,因此在出栈操作时,最大频率的元素中的最接近栈顶的元素最先被移除,符合题目的要求。
- 用
maxFreq
extit{maxFreq}
maxFreq 表示最大频率,将最大频率对应的栈的栈顶元素移除之后,被移除的元素的频率变成
maxFreq
−
1
extit{maxFreq} - 1
maxFreq−1,因此最大频率栈内的其余元素的最大频率至少为
maxFreq
−
1
extit{maxFreq} - 1
maxFreq−1。移除最大频率的元素之后,可能有两种情况:
- 如果只有一个最大频率元素,则该元素被移除之后, maxFreq extit{maxFreq} maxFreq 对应的栈为空,此时最大频率变成 maxFreq − 1 extit{maxFreq} - 1 maxFreq−1;
- 如果有多个最大频率元素,则该元素被移除之后, maxFreq extit{maxFreq} maxFreq 对应的栈不为空,最大频率仍为 maxFreq extit{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 是最大频率栈的元素个数。需要使用两个哈希表分别记录每个元素值的频率以及每个频率对应的元素,每个哈希表中的元素个数都不会超过最大频率栈的元素个数。