Maximum Frequency Stack
Stack returning the most frequent element on pop, ties to the most recent — bucket stacks by frequency.
3 min read
hash-map stack design
Problem#
Design FreqStack:
push(val)— push.pop()— return and remove the most frequent value. Ties broken by most recently pushed.
Examples#
- After
push(5), push(7), push(5), push(7), push(4), push(5), the stack reads[5,7,5,7,4,5]. Pops yield5, 7, 5, 4, 5, 7.
Constraints#
0 <= val <= 10^9, up to2 * 10^4calls.
Hints#
Hint
Maintain one stack per frequency level. On push, append to
stacks[freq[val]++]. On pop, take the top of the deepest stack. Approach#
Two maps: freq[val] = current count, and a vector of stacks group[k] = values seen at least k times in push order. On push, increment freq[val] to k, then push to group[k]. On pop, take the top of the deepest stack; decrement freq[val]; clean up empty stacks.
Solution#
class FreqStack { unordered_map<int,int> freq; vector<stack<int>> group;public: void push(int val) { int k = ++freq[val]; if ((int)group.size() < k) group.emplace_back(); group[k - 1].push(val); }
int pop() { int val = group.back().top(); group.back().pop(); if (group.back().empty()) group.pop_back(); --freq[val]; return val; }};type FreqStack struct { freq map[int]int group [][]int}
func Constructor() FreqStack { return FreqStack{freq: map[int]int{}}}
func (s *FreqStack) Push(val int) { s.freq[val]++ k := s.freq[val] if len(s.group) < k { s.group = append(s.group, []int{}) } s.group[k-1] = append(s.group[k-1], val)}
func (s *FreqStack) Pop() int { top := len(s.group) - 1 stack := s.group[top] val := stack[len(stack)-1] s.group[top] = stack[:len(stack)-1] if len(s.group[top]) == 0 { s.group = s.group[:top] } s.freq[val]-- return val}from collections import defaultdictfrom typing import List
class FreqStack: def __init__(self): self.freq: dict = defaultdict(int) self.group: List[List[int]] = []
def push(self, val: int) -> None: self.freq[val] += 1 k = self.freq[val] if len(self.group) < k: self.group.append([]) self.group[k - 1].append(val)
def pop(self) -> int: val = self.group[-1].pop() if not self.group[-1]: self.group.pop() self.freq[val] -= 1 return valclass FreqStack { constructor() { this.freq = new Map(); this.group = []; }
push(val) { const k = (this.freq.get(val) ?? 0) + 1; this.freq.set(val, k); if (this.group.length < k) this.group.push([]); this.group[k - 1].push(val); }
pop() { const top = this.group[this.group.length - 1]; const val = top.pop(); if (top.length === 0) this.group.pop(); this.freq.set(val, this.freq.get(val) - 1); return val; }}class FreqStack { private Map<Integer, Integer> freq; private List<Deque<Integer>> group;
public FreqStack() { freq = new HashMap<>(); group = new ArrayList<>(); }
public void push(int val) { int k = freq.getOrDefault(val, 0) + 1; freq.put(val, k); if (group.size() < k) group.add(new ArrayDeque<>()); group.get(k - 1).push(val); }
public int pop() { Deque<Integer> top = group.get(group.size() - 1); int val = top.pop(); if (top.isEmpty()) group.remove(group.size() - 1); freq.put(val, freq.get(val) - 1); return val; }}class FreqStack { private freq: Map<number, number> = new Map(); private group: number[][] = [];
push(val: number): void { const k = (this.freq.get(val) ?? 0) + 1; this.freq.set(val, k); if (this.group.length < k) this.group.push([]); this.group[k - 1].push(val); }
pop(): number { const top = this.group[this.group.length - 1]; const val = top.pop() as number; if (top.length === 0) this.group.pop(); this.freq.set(val, (this.freq.get(val) as number) - 1); return val; }}Editorial#
The crucial insight: when we push val for the k-th time, we add it to group[k-1] — but val’s earlier copies remain in group[0..k-2]. On pop, we always take from the deepest non-empty stack, which represents the current max frequency. Each operation touches one stack and one map entry — O(1).
Complexity#
- Time: O(1) amortized per
push/pop. - Space: O(N).
Concept revision#
Related#