Maximum Frequency Stack

Design a stack-like container FreqStack whose pop removes the value that appears most often instead of simply the newest one.

  • FreqStack() creates an empty container.
  • push(val) adds the integer val on top.
  • pop() removes and returns one copy of the value that currently occurs the greatest number of times. If several values share that highest count, remove the copy that is closest to the top (the one pushed most recently among them).

pop is only called when the container is not empty.

Each test is a list of operation names and a matching list of argument lists; the expected output lists every return value, with null for the constructor and push.

Example 1

Input: ["FreqStack","push","push","push","push","push","push","pop","pop","pop","pop"] [[],[4],[7],[4],[9],[7],[4],[],[],[],[]] Output: [null,null,null,null,null,null,null,4,7,4,9] Why: After the pushes, 4 appears three times, 7 twice and 9 once. The pops return 4, then 7 (tied with 4 at two copies, but pushed more recently), then 4, then 9 (all values now appear once and 9 is on top).

Example 2

Input: ["FreqStack","push","pop"] [[],[5],[]] Output: [null,null,5] Why: One push and one pop return the only value.

Example 3

Input: ["FreqStack","push","push","push","pop","push","push","pop","pop","pop","pop"] [[],[1],[2],[2],[],[3],[1],[],[],[],[]] Output: [null,null,null,null,2,null,null,1,3,2,1] Why: 2 is the most frequent and is popped first. After pushing 3 and 1, the value 1 has two copies, so it goes next; the rest come out newest first.

Constraints

  • 0 <= val <= 109
  • At most 2 × 104 calls to push and pop in total.
  • pop is never called on an empty container.

Pattern Path · Design · problem 168 of 169

You need to know each value's current count, so start with a hash map from value to frequency. The hard part is the tie-break by recency.
Imagine giving every pushed copy a label: "the f-th copy of val". Which copy should the next pop remove?
Keep one ordinary stack per frequency level: when val reaches frequency f, push it onto stack f. Popping from the highest non-empty level gives the most frequent value, and within a level the newest one is on top.
Target: O(1) per push and pop time, O(n) extra space.

Start simple

Store everything in a list and, on each pop, count frequencies and scan from the top for the first value with the maximum count: O(n) per pop.

The key idea

Two questions must be answered on every pop: which frequency is highest, and among values with that frequency, which is closest to the top. A heap ordered by (frequency, push time) works in O(log n), but there is a neater constant-time structure.

When val is pushed and its count becomes f, append it to groups[f], a plain stack for frequency level f. A value with count 3 therefore sits in levels 1, 2 and 3 at once. Track maxFreq, the highest non-empty level.

To pop, take the top of groups[maxFreq]. It has the highest frequency by definition, and it is the most recent of its level because each level is ordered by when values reached that count. Decrement its frequency. If that level is now empty, decrease maxFreq by one. This is safe: every value that was at level f is still present at level f-1, so the level below is never empty.

Notice that we never move an entry between levels. Removing the top copy simply exposes the value's lower-level entry, which is exactly the state it would be in had the last push never happened.

Complexity

  • Time: O(1) per push and pop
  • Space: O(n)

Python

from collections import Counter, defaultdict


class FreqStack:
    def __init__(self):
        self.freq = Counter()             # value -> copies currently in the stack
        self.groups = defaultdict(list)   # f -> values that reached frequency f, oldest first
        self.max_freq = 0

    def push(self, val: int) -> None:
        self.freq[val] += 1
        f = self.freq[val]
        self.groups[f].append(val)        # this copy is the f-th copy of val
        self.max_freq = max(self.max_freq, f)

    def pop(self) -> int:
        # The newest value in the highest group is both most frequent and closest to the top.
        val = self.groups[self.max_freq].pop()
        self.freq[val] -= 1
        if not self.groups[self.max_freq]:
            self.max_freq -= 1
        return val

JavaScript

class FreqStack {
  constructor() {
    this.freq = new Map(); // value -> copies currently in the stack
    this.groups = [[]];    // groups[f] = values that reached frequency f, oldest first (index 0 unused)
  }

  push(val) {
    const f = (this.freq.get(val) || 0) + 1;
    this.freq.set(val, f);
    if (f === this.groups.length) this.groups.push([]);
    this.groups[f].push(val); // this copy is the f-th copy of val
  }

  pop() {
    // The newest value in the highest group is both most frequent and closest to the top.
    const top = this.groups[this.groups.length - 1];
    const val = top.pop();
    this.freq.set(val, this.freq.get(val) - 1);
    if (top.length === 0) this.groups.pop();
    return val;
  }
}

What the interviewer is checking

The interviewer is checking whether you can find a layered structure that makes both priorities (count, then recency) fall out naturally, instead of reaching straight for a heap and stopping there.

Mistakes to avoid

  • Breaking ties by the time a value was first pushed instead of the time it reached the tied frequency.
  • Scanning all values on every pop, which is linear per operation.
  • Forgetting to lower maxFreq once its level becomes empty.
  • Removing a value from all levels on pop; only the top-level copy should go.

Follow-ups to expect

  • How would a heap-based version work? (Key: frequency, then push sequence number.)
  • Add a peek() that does not remove. (Read the top of the max level.)
  • Support removing the least frequent value instead. (Track a min level; harder after pops.)

In real software

Bucketing items by count with a stack per bucket is how LFU caches and "most active" feeds pick a victim or a highlight in constant time while still respecting recency.

Practice next

Next Post Previous Post
No Comment
Add Comment
comment url