Mutating counts without cleanup
Wrong move: Zero-count keys stay in map and break distinct/count constraints.
Usually fails on: Window/map size checks are consistently off by one.
Fix: Delete keys when count reaches zero.
Break down a hard problem into reliable checkpoints, edge-case handling, and complexity trade-offs.
Design a stack-like data structure to push elements to the stack and pop the most frequent element from the stack.
Implement the FreqStack class:
FreqStack() constructs an empty frequency stack.void push(int val) pushes an integer val onto the top of the stack.int pop() removes and returns the most frequent element in the stack.
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 FreqStack freqStack = new FreqStack(); freqStack.push(5); // The stack is [5] freqStack.push(7); // The stack is [5,7] freqStack.push(5); // The stack is [5,7,5] freqStack.push(7); // The stack is [5,7,5,7] freqStack.push(4); // The stack is [5,7,5,7,4] freqStack.push(5); // The stack is [5,7,5,7,4,5] freqStack.pop(); // return 5, as 5 is the most frequent. The stack becomes [5,7,5,7,4]. freqStack.pop(); // return 7, as 5 and 7 is the most frequent, but 7 is closest to the top. The stack becomes [5,7,5,4]. freqStack.pop(); // return 5, as 5 is the most frequent. The stack becomes [5,7,4]. freqStack.pop(); // return 4, as 4, 5 and 7 is the most frequent, but 4 is closest to the top. The stack becomes [5,7].
Constraints:
0 <= val <= 1092 * 104 calls will be made to push and pop.pop.Problem summary: Design a stack-like data structure to push elements to the stack and pop the most frequent element from the stack. Implement the FreqStack class: FreqStack() constructs an empty frequency stack. void push(int val) pushes an integer val onto the top of the stack. int pop() removes and returns the most frequent element in the stack. If there is a tie for the most frequent element, the element closest to the stack's top is removed and returned.
Start with the most direct exhaustive search. That gives a correctness anchor before optimizing.
Pattern signal: Hash Map · Stack · Design · Segment Tree
["FreqStack","push","push","push","push","push","push","pop","pop","pop","pop"] [[],[5],[7],[5],[7],[4],[5],[],[],[],[]]
Source-backed implementations are provided below for direct study and interview prep.
// Accepted solution for LeetCode #895: Maximum Frequency Stack
class FreqStack {
private Map<Integer, Integer> cnt = new HashMap<>();
private PriorityQueue<int[]> q
= new PriorityQueue<>((a, b) -> a[0] == b[0] ? b[1] - a[1] : b[0] - a[0]);
private int ts;
public FreqStack() {
}
public void push(int val) {
cnt.put(val, cnt.getOrDefault(val, 0) + 1);
q.offer(new int[] {cnt.get(val), ++ts, val});
}
public int pop() {
int val = q.poll()[2];
cnt.put(val, cnt.get(val) - 1);
return val;
}
}
/**
* Your FreqStack object will be instantiated and called as such:
* FreqStack obj = new FreqStack();
* obj.push(val);
* int param_2 = obj.pop();
*/
// Accepted solution for LeetCode #895: Maximum Frequency Stack
type FreqStack struct {
cnt map[int]int
q hp
ts int
}
func Constructor() FreqStack {
return FreqStack{map[int]int{}, hp{}, 0}
}
func (this *FreqStack) Push(val int) {
this.cnt[val]++
this.ts++
heap.Push(&this.q, tuple{this.cnt[val], this.ts, val})
}
func (this *FreqStack) Pop() int {
val := heap.Pop(&this.q).(tuple).val
this.cnt[val]--
return val
}
type tuple struct{ cnt, ts, val int }
type hp []tuple
func (h hp) Len() int { return len(h) }
func (h hp) Less(i, j int) bool {
return h[i].cnt > h[j].cnt || h[i].cnt == h[j].cnt && h[i].ts > h[j].ts
}
func (h hp) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
func (h *hp) Push(v any) { *h = append(*h, v.(tuple)) }
func (h *hp) Pop() any { a := *h; v := a[len(a)-1]; *h = a[:len(a)-1]; return v }
/**
* Your FreqStack object will be instantiated and called as such:
* obj := Constructor();
* obj.Push(val);
* param_2 := obj.Pop();
*/
# Accepted solution for LeetCode #895: Maximum Frequency Stack
class FreqStack:
def __init__(self):
self.cnt = defaultdict(int)
self.q = []
self.ts = 0
def push(self, val: int) -> None:
self.ts += 1
self.cnt[val] += 1
heappush(self.q, (-self.cnt[val], -self.ts, val))
def pop(self) -> int:
val = heappop(self.q)[2]
self.cnt[val] -= 1
return val
# Your FreqStack object will be instantiated and called as such:
# obj = FreqStack()
# obj.push(val)
# param_2 = obj.pop()
// Accepted solution for LeetCode #895: Maximum Frequency Stack
use std::collections::HashMap;
struct FreqStack {
count: HashMap<i32, i32>,
max_count: i32,
stacks: HashMap<i32, Vec<i32>>,
}
impl FreqStack {
fn new() -> Self {
Self {
count: HashMap::new(),
max_count: 0,
stacks: HashMap::new(),
}
}
fn push(&mut self, val: i32) {
let val_count = 1 + *self.count.get(&val).or(Some(&0)).unwrap();
self.count.insert(val, val_count);
if val_count > self.max_count {
self.max_count = val_count;
self.stacks.insert(val_count, vec![]);
}
self.stacks.get_mut(&val_count).unwrap().push(val);
}
fn pop(&mut self) -> i32 {
let res = self.stacks.get_mut(&self.max_count).unwrap().pop().unwrap();
*self.count.get_mut(&res).unwrap() -= 1;
if self.stacks.get(&self.max_count).unwrap().is_empty() {
self.max_count -= 1;
}
res
}
}
// Accepted solution for LeetCode #895: Maximum Frequency Stack
class FreqStack {
cnt: Map<number, number>;
maxCnt: number;
stacks: Map<number, number[]>;
constructor() {
this.cnt = new Map();
this.maxCnt = 0;
this.stacks = new Map();
}
push(val: number): void {
let valCnt: number = 1 + (this.cnt.get(val) || 0);
this.cnt.set(val, valCnt);
if (valCnt > this.maxCnt) {
this.maxCnt = valCnt;
this.stacks.set(valCnt, []);
}
this.stacks.get(valCnt)?.push(val);
}
pop(): number {
let res: number = this.stacks.get(this.maxCnt)?.pop()!;
this.cnt.set(res, this.cnt.get(res)! - 1);
if (this.stacks.get(this.maxCnt)?.length == 0) {
this.maxCnt -= 1;
}
return res;
}
}
Use this to step through a reusable interview workflow for this problem.
For each element, scan left (or right) to find the next greater/smaller element. The inner scan can visit up to n elements per outer iteration, giving O(n²) total comparisons. No extra space needed beyond loop variables.
Each element is pushed onto the stack at most once and popped at most once, giving 2n total operations = O(n). The stack itself holds at most n elements in the worst case. The key insight: amortized O(1) per element despite the inner while-loop.
Review these before coding to avoid predictable interview regressions.
Wrong move: Zero-count keys stay in map and break distinct/count constraints.
Usually fails on: Window/map size checks are consistently off by one.
Fix: Delete keys when count reaches zero.
Wrong move: Pushing without popping stale elements invalidates next-greater/next-smaller logic.
Usually fails on: Indices point to blocked elements and outputs shift.
Fix: Pop while invariant is violated before pushing current element.