Watch 7 video solutions for Dinner Plate Stacks, a hard level problem involving Hash Table, Stack, Design. This walkthrough by Programming Live with Larry has 2,293 views views. Want to try solving it yourself? Practice on FleetCode or read the detailed text solution.
You have an infinite number of stacks arranged in a row and numbered (left to right) from 0, each of the stacks has the same maximum capacity.
Implement the DinnerPlates class:
DinnerPlates(int capacity) Initializes the object with the maximum capacity of the stacks capacity.void push(int val) Pushes the given integer val into the leftmost stack with a size less than capacity.int pop() Returns the value at the top of the rightmost non-empty stack and removes it from that stack, and returns -1 if all the stacks are empty.int popAtStack(int index) Returns the value at the top of the stack with the given index index and removes it from that stack or returns -1 if the stack with that given index is empty.
Example 1:
Input
["DinnerPlates", "push", "push", "push", "push", "push", "popAtStack", "push", "push", "popAtStack", "popAtStack", "pop", "pop", "pop", "pop", "pop"]
[[2], [1], [2], [3], [4], [5], [0], [20], [21], [0], [2], [], [], [], [], []]
Output
[null, null, null, null, null, null, 2, null, null, 20, 21, 5, 4, 3, 1, -1]
Explanation:
DinnerPlates D = DinnerPlates(2); // Initialize with capacity = 2
D.push(1);
D.push(2);
D.push(3);
D.push(4);
D.push(5); // The stacks are now: 2 4
1 3 5
﹈ ﹈ ﹈
D.popAtStack(0); // Returns 2. The stacks are now: 4
1 3 5
﹈ ﹈ ﹈
D.push(20); // The stacks are now: 20 4
1 3 5
﹈ ﹈ ﹈
D.push(21); // The stacks are now: 20 4 21
1 3 5
﹈ ﹈ ﹈
D.popAtStack(0); // Returns 20. The stacks are now: 4 21
1 3 5
﹈ ﹈ ﹈
D.popAtStack(2); // Returns 21. The stacks are now: 4
1 3 5
﹈ ﹈ ﹈
D.pop() // Returns 5. The stacks are now: 4
1 3
﹈ ﹈
D.pop() // Returns 4. The stacks are now: 1 3
﹈ ﹈
D.pop() // Returns 3. The stacks are now: 1
﹈
D.pop() // Returns 1. There are no stacks.
D.pop() // Returns -1. There are still no stacks.
Constraints:
1 <= capacity <= 2 * 1041 <= val <= 2 * 1040 <= index <= 1052 * 105 calls will be made to push, pop, and popAtStack.Problem Overview: You need to design a data structure that simulates stacks of dinner plates. Each stack has a fixed capacity. When pushing, the value must go into the leftmost stack that is not full. When popping, values are removed from the rightmost non‑empty stack. The structure must also support popAtStack(index) which pops from a specific stack.
Approach 1: Brute Force Approach (Push/Pop Scan) (Time: O(n), Space: O(n))
The simplest implementation stores stacks in an array or list. For push, iterate from the beginning until you find the first stack whose size is below the capacity. For pop, iterate from the end until you find a non‑empty stack. popAtStack directly accesses the requested index if it exists. This approach is easy to implement but inefficient because every push or pop may scan multiple stacks. The total space grows with the number of elements since every stack is stored explicitly.
Approach 2: Using List with Direct Indexing (Time: O(n) worst case, Space: O(n))
This design still stores stacks in a list but optimizes stack access using direct indexing. Each stack is represented as a list and new stacks are appended when needed. push tries to place elements in earlier stacks when possible, while pop removes from the last stack and trims empty stacks from the end. Although worst‑case scanning still exists, direct indexing simplifies operations like popAtStack. The structure relies on basic stack behavior and sequential storage.
Approach 3: Using Two Priority Queues (Time: O(log n), Space: O(n))
This is the common interview solution. Maintain two heaps: a min‑heap storing indices of stacks that are not full, and a max‑heap storing indices of stacks that are not empty. When pushing, extract the smallest index from the available heap and insert the element into that stack. When popping, remove from the stack with the largest index using the max‑heap. Heaps automatically track the correct stack positions, ensuring operations stay logarithmic. This approach combines priority queues with indexed stacks and avoids expensive scans.
Approach 4: Optimized Approach Using Mathematical Properties (Time: O(1)–O(log n), Space: O(n))
Another optimization uses predictable stack positions derived from capacity and insertion order. Instead of scanning stacks, maintain structural properties about where the next available slot should be. The logic tracks partially filled stacks and reuses them efficiently when popAtStack creates gaps. Mathematical indexing removes redundant checks and keeps push/pop close to constant time in many scenarios. This version still stores stacks explicitly but minimizes bookkeeping overhead compared with heap-heavy designs.
Recommended for interviews: The two‑priority‑queue solution is usually what interviewers expect. It clearly demonstrates how to manage dynamic resources with heaps and indexed stacks. Showing the brute force version first proves you understand the constraints, but implementing the heap-based design highlights stronger skills with data structure coordination and efficient state tracking.
| Approach | Time | Space | When to Use |
|---|---|---|---|
| Brute Force Scan | O(n) per push/pop | O(n) | Good for understanding the problem or quick prototypes |
| List with Direct Indexing | O(n) worst case | O(n) | Simpler implementation with predictable stack indexing |
| Two Priority Queues | O(log n) | O(n) | Best general solution; efficient push and pop operations |
| Mathematical Optimization | O(1)–O(log n) | O(n) | When minimizing heap operations or leveraging predictable stack indices |