Quick Overview

Phone screen coding problem: process push and pop operations on a stack in which every pop removes the value that currently occurs most often, breaking ties by the tied value closest to the top. It tests designing a data structure that tracks frequency and recency together efficiently.

Stack Whose Pop Removes the Most Frequent Value, Ties Broken by Recency

Company: Amazon

Role: Software Engineer

Category: Coding & Algorithms

Difficulty: medium

Interview Round: Onsite

A frequency stack holds integers and supports two operations: - **push x**: place `x` on top of the stack. - **pop**: remove and return the value that occurs most often in the stack right now. If several values tie for the highest count, choose the one that has an occurrence closest to the top of the stack. Only that value's topmost occurrence is removed. Given a sequence of operations, return the values returned by the pop operations, in order. ### Function Signature ```python def frequency_stack(operations: list[tuple[str, int]]) -> list[int]: ``` Each operation is `("push", x)` or `("pop", 0)`. The integer in a pop operation is always `0` and is ignored. ### Rules - The count of a value is the number of its occurrences currently in the stack. Occurrences already removed by earlier pops do not count. - Among the values tied for the highest count, pick the one whose topmost occurrence is nearest the top of the stack, then remove exactly that occurrence. All other elements keep their order. - A pop is issued only when the stack is not empty. - If there are no pop operations, return an empty list. ### Constraints - `1 <= len(operations) <= 10^5` - `-10^9 <= x <= 10^9` for every pushed value - Every pop is applied to a non-empty stack. ### Examples **Example 1** ```text Input: operations = [("push", 3), ("push", 8), ("push", 3), ("push", 8), ("push", 1), ("push", 3), ("pop", 0), ("pop", 0), ("pop", 0), ("pop", 0)] Output: [3, 8, 3, 1] ``` The stack, bottom to top, is 3, 8, 3, 8, 1, 3. The first pop returns 3, which occurs three times, and leaves 3, 8, 3, 8, 1. Now 3 and 8 both occur twice, and the topmost 8 is above the topmost 3, so 8 is returned, leaving 3, 8, 3, 1. Next 3 is the only value that occurs twice, leaving 3, 8, 1. Finally 3, 8 and 1 each occur once, and 1 is on top. **Example 2** ```text Input: operations = [("push", 2), ("push", 2), ("push", 9), ("pop", 0), ("push", 9), ("pop", 0), ("pop", 0), ("pop", 0)] Output: [2, 9, 9, 2] ``` After three pushes the stack is 2, 2, 9. The first pop returns 2 and leaves 2, 9. Pushing 9 gives 2, 9, 9, so 9 now occurs most often and is returned, leaving 2, 9. Then 2 and 9 each occur once, and 9 is on top. The last pop returns 2. **Example 3** ```text Input: operations = [("push", -1), ("pop", 0), ("push", -1), ("push", 6), ("push", 6), ("push", -1), ("pop", 0)] Output: [-1, -1] ``` The first pop empties the stack. Afterwards the stack is -1, 6, 6, -1: both values occur twice, and the topmost -1 is above the topmost 6.

Overview: Phone screen coding problem: process push and pop operations on a stack in which every pop removes the value that currently occurs most often, breaking ties by the tied value closest to the top. It tests designing a data structure that tracks frequency and recency together efficiently.

A frequency stack holds integers and supports two operations: - **push x**: place `x` on top of the stack. - **pop**: remove and return the value that occurs most often in the stack right now. If several values tie for the highest count, choose the one that has an occurrence closest to the top of the stack. Only that value's topmost occurrence is removed. Implement `frequency_stack(operations)`. `operations` is a list of operations applied in order to an initially empty stack. Each operation is a pair `("push", x)` or `("pop", 0)`; the integer in a pop operation is always `0` and is ignored. Return the values returned by the pop operations, in the order the pops occur. **Rules** - The count of a value is the number of its occurrences currently in the stack. Occurrences already removed by earlier pops do not count. - Among the values tied for the highest count, pick the one whose topmost occurrence is nearest the top of the stack, then remove exactly that occurrence. All other elements keep their order. - A pop is issued only when the stack is not empty. - If there are no pop operations, return an empty list. **Example 1** ```text Input: operations = [("push", 3), ("push", 8), ("push", 3), ("push", 8), ("push", 1), ("push", 3), ("pop", 0), ("pop", 0), ("pop", 0), ("pop", 0)] Output: [3, 8, 3, 1] ``` The stack, bottom to top, is 3, 8, 3, 8, 1, 3. The first pop returns 3, which occurs three times, and leaves 3, 8, 3, 8, 1. Now 3 and 8 both occur twice, and the topmost 8 is above the topmost 3, so 8 is returned, leaving 3, 8, 3, 1. Next 3 is the only value that occurs twice, leaving 3, 8, 1. Finally 3, 8 and 1 each occur once, and 1 is on top. **Example 2** ```text Input: operations = [("push", 2), ("push", 2), ("push", 9), ("pop", 0), ("push", 9), ("pop", 0), ("pop", 0), ("pop", 0)] Output: [2, 9, 9, 2] ``` After three pushes the stack is 2, 2, 9. The first pop returns 2 and leaves 2, 9. Pushing 9 gives 2, 9, 9, so 9 now occurs most often and is returned, leaving 2, 9. Then 2 and 9 each occur once, and 9 is on top. The last pop returns 2. **Constraints** - `1 <= len(operations) <= 10^5` - `-10^9 <= x <= 10^9` for every pushed value - Each operation is `("push", x)` or `("pop", 0)`. - Every pop is applied to a non-empty stack. Every pushed and returned value fits in a signed 32-bit integer.

Constraints

  • 1 <= len(operations) <= 10^5
  • -10^9 <= x <= 10^9 for every pushed value
  • Each operation is ("push", x) or ("pop", 0); the integer in a pop operation is always 0 and is ignored
  • Every pop is applied to a non-empty stack

Examples

Input: ([('push', 5)],)

Expected Output: []

Explanation: Minimum valid: a single push and no pops returns an empty list.

Input: ([('push', 7), ('pop', 0)],)

Expected Output: [7]

Explanation: Single push then single pop returns the only value.

Hints

  1. Only occurrences still in the stack count: every push raises one value's count by one, and every pop lowers one value's count by one.
  2. When several values share the highest count, compare where their topmost occurrences sit; the one pushed most recently among those occurrences is removed.
  3. A value whose occurrences have all been popped starts again from a count of zero.

Loading coding console...

Show the approach

Approach

Keep counts[v], the number of occurrences of v currently in the stack, and a list of levels: levels[k-1] is a stack of the values whose current count is at least k, ordered by when each one's k-th current occurrence was pushed. The number of levels equals the highest current count.

Push x: raise counts[x] to c. Because counts only grow by one, c is at most one more than the number of levels, so open level c if it does not exist yet, then push x onto levels[c-1]. Pop: take the top value of the highest level, lower its count by one, drop that level if it became empty, and record the value.

Why it is correct: a value with count c has its c-th occurrence as its topmost occurrence, and that push is exactly the one that put it onto level c. So the highest level holds exactly the values tied for the highest count, ordered by the positions of their topmost occurrences, and its top is the value whose topmost occurrence is nearest the top of the stack. Removing that occurrence lowers the value's count to c-1; its entry on level c-1 records its (c-1)-th occurrence, which is now its topmost, so the invariant still holds. Entries are only ever removed from the top of a level, so every level stays in push order, and no other element of the stack is touched, so the remaining order is preserved.

Edge cases: no pop operations returns an empty list; a value whose occurrences have all been popped starts again from count 1 when it is pushed again; values of equal magnitude and opposite sign are different keys; every value fits in a signed 32-bit integer.

Time complexity:
O(n), where n = len(operations); each push and pop is O(1) expected (one hash-map update and one list push or pop).
Space complexity:
O(n) for the counts map and the level stacks, which together hold one entry per element currently in the stack, plus the output list.