Maximum Frequency Stack
Problem Description
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 integervalonto 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.
Examples
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. The stack becomes [5,7,4]. freqStack.pop(); // return 4. The stack becomes [5,7].
Constraints
0 <= val <= 10⁹- At most
2 * 10⁴calls will be made topushandpop. - It is guaranteed that there are elements in the stack before calling
pop.
Grouping Stacks by Frequencies
If we use a priority queue to track frequencies, push/pop operations take O(log n) time. To implement both push and pop in O(1) time, we can map frequencies to stacks. We maintain:
- A hash map
freqto count the frequency of each element. - A hash map
groupwhere the key is the frequency, and the value is a stack of elements that have reached that frequency. - An integer
maxFreqto track the overall maximum frequency. When pushingval:
- Update its frequency:
f = freq[val] + 1. - Add
valtogroup[f](which is a stack). - Update
maxFreq = max(maxFreq, f). When popping: - Pop from the stack at
group[maxFreq]. - Decrement its frequency in
freq. - If the stack at
group[maxFreq]is empty, decrementmaxFreq.
Solution 1: Frequency Grouping Stacks
Map frequencies to individual element stacks to execute push and pop in O(1) time.
import java.util.HashMap;
import java.util.Stack;
class FreqStack {
private HashMap<Integer, Integer> freq;
private HashMap<Integer, Stack<Integer>> group;
private int maxFreq;
public FreqStack() {
freq = new HashMap<>();
group = new HashMap<>();
maxFreq = 0;
}
public void push(int val) {
int f = freq.getOrDefault(val, 0) + 1;
freq.put(val, f);
maxFreq = Math.max(maxFreq, f);
group.putIfAbsent(f, new Stack<>());
group.get(f).push(val);
}
public int pop() {
int val = group.get(maxFreq).pop();
freq.put(val, freq.get(val) - 1);
if (group.get(maxFreq).isEmpty()) {
maxFreq--; // reduce maximum frequency if current level is empty
}
return val;
}
}from collections import Counter
class FreqStack:
def __init__(self):
self.freq = Counter()
self.group = {}
self.max_freq = 0
def push(self, val: int) -> None:
f = self.freq[val] + 1
self.freq[val] = f
self.max_freq = max(self.max_freq, f)
if f not in self.group:
self.group[f] = []
self.group[f].append(val)
def pop(self) -> int:
val = self.group[self.max_freq].pop()
self.freq[val] -= 1
if not self.group[self.max_freq]:
self.max_freq -= 1 # reduce maximum frequency level
return val#include <unordered_map>
#include <stack>
#include <algorithm>
class FreqStack {
private:
std::unordered_map<int, int> freq;
std::unordered_map<int, std::stack<int>> group;
int maxFreq;
public:
FreqStack() {
maxFreq = 0;
}
void push(int val) {
int f = ++freq[val];
maxFreq = std::max(maxFreq, f);
group[f].push(val);
}
int pop() {
int val = group[maxFreq].top();
group[maxFreq].pop();
freq[val]--;
if (group[maxFreq].empty()) {
maxFreq--;
}
return val;
}
};Complexity Analysis
- Time Complexity: O(1) for both
pushandpopoperations since hash map lookups and stack push/pop take constant time. - Space Complexity: O(n) auxiliary space to store elements in the frequency and grouping maps.
Where It Breaks
This solution uses considerable memory, allocating multiple stack lists. If memory constraints are tight, a priority queue is preferred, sacrificing runtime speeds (O(log n)) for better space conservation.
Common Mistakes
- Incorrect pop logic: Attempting to sort or search the frequency list during a pop operation, which raises the time complexity to O(n) and causes timeouts.
- Max frequency tracking errors: Not decrementing
maxFreqwhen the highest frequency stack becomes empty.
Frequently Asked Questions
Why does this tie-breaker work automatically?
Because each element that reaches a frequency level f is pushed onto group[f] in chronological order. Popping from group[f] naturally returns the element closest to the top of the stack first.
Can an element reside in multiple frequency stacks simultaneously?
Yes. If element 5 has a frequency of 3, it will have been pushed to group[1], group[2], and group[3]. This handles its occurrences independently.