Maximum Frequency Stack
Maximum Frequency Stack: 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 integerval
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.
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 <= 10^9
- At most
2 * 10^4
calls will be made topush
andpop
. - It is guaranteed that there will be at least one element in the stack before calling
pop
.
Try this Problem on your own or check similar problems:
Solution:
- Java
- JavaScript
- Python
- C++
class FreqStack {
Map<Integer, Integer> freqMap;
List<Stack<Integer>> buckets;
public FreqStack() {
freqMap = new HashMap<>();
buckets = new ArrayList<>();
}
public void push(int val) {
int freq = freqMap.getOrDefault(val, 0) + 1;
if(freq > buckets.size()){
Stack<Integer> s = new Stack<>(){};
s.push(val);
buckets.add(s);
}else{
buckets.get(freq - 1).push(val);
}
freqMap.put(val, freq);
}
public int pop() {
int val = buckets.get(buckets.size() - 1).pop();
if(freqMap.get(val) - 1 == 0) freqMap.remove(val);
else freqMap.put(val, freqMap.get(val) - 1);
if(buckets.get(buckets.size() - 1).empty()) buckets.remove(buckets.size() - 1);
return val;
}
}
var FreqStack = function () {
this.freqMap = new Map();
this.buckets = [];
};
/**
* @param {number} val
* @return {void}
*/
FreqStack.prototype.push = function (val) {
const freq = (this.freqMap.get(val) || 0) + 1;
if (!this.buckets[freq]) {
this.buckets[freq] = [];
}
this.buckets[freq].push(val);
this.freqMap.set(val, freq);
};
/**
* @return {number}
*/
FreqStack.prototype.pop = function () {
const maxFreq = this.buckets.length - 1;
const stack = this.buckets[maxFreq];
const val = stack.pop();
if (stack.length === 0) {
this.buckets.pop();
}
this.freqMap.set(val, maxFreq - 1);
return val;
};
/**
* Your FreqStack object will be instantiated and called as such:
* var obj = new FreqStack()
* obj.push(val)
* var param_2 = obj.pop()
*/
class FreqStack:
def __init__(self):
self.freqMap = {}
self.buckets = []
def push(self, val: int) -> None:
freq = self.freqMap.get(val, 0) + 1
if freq > len(self.buckets):
s = []
s.append(val)
self.buckets.append(s)
else:
self.buckets[freq - 1].append(val)
self.freqMap[val] = freq
def pop(self) -> int:
stack = self.buckets[-1]
val = stack.pop()
if self.freqMap[val] - 1 == 0:
del self.freqMap[val]
else:
self.freqMap[val] = self.freqMap[val] - 1
if len(stack) == 0:
self.buckets.pop()
return val
# Your FreqStack object will be instantiated and called as such:
# obj = FreqStack()
# obj.push(val)
# param_2 = obj.pop()
class FreqStack {
public:
FreqStack() {
}
void push(int val) {
int freq = freqMap[val] + 1;
if (freq > buckets.size()) {
std::stack<int> s;
s.push(val);
buckets.push_back(s);
} else {
buckets[freq - 1].push(val);
}
freqMap[val] = freq;
}
int pop() {
std::stack<int>& stack = buckets[buckets.size() - 1];
int val = stack.top();
stack.pop();
if (freqMap[val] - 1 == 0) {
freqMap.erase(val);
} else {
freqMap[val] = freqMap[val] - 1;
}
if (stack.empty()) {
buckets.pop_back();
}
return val;
}
private:
std::unordered_map<int, int> freqMap;
std::vector<std::stack<int>> buckets;
};
/**
* Your FreqStack object will be instantiated and called as such:
* FreqStack* obj = new FreqStack();
* obj->push(val);
* int param_2 = obj->pop();
*/
Time/Space Complexity:
- Time Complexity: O(1)
- Space Complexity: O(n)
Explanation:
It's a federation of data structures we use to solve this problem:
- We keep a
freqMap
which keep track of occurrences of all numbers added to stack. buckets
list of stacks, one stack per frequency (like in bucket sort).
We have linear space complexity since we at most keep track of n
elements added to the stack with constant time complexity. For the push
we check the frequency of the val
and if it can be placed in one of the frequency buckets. If it can't we create a new bucket with a new stack and push the val
to it. We update the freqMap
accordingly. For pop
we get the last bucket and top of that bucket's stack and we return that value, while also checking if the bucket should be removed if it's empty and again update the freqMap
counts.