Bird
Raised Fist0
Interview Prepstacks-monotonic-stackmediumAmazonGoogleFacebookBloomberg

Minimum Stack (Min in O(1))

Choose your preparation mode4 modes available
📋
Problem

Imagine a stack of plates where you want to quickly know the smallest plate at any time without sorting or scanning all plates.

Design a stack that supports push, pop, top, and retrieving the minimum element in constant time. Implement the following operations: push(x) - Push element x onto stack; pop() - Removes the element on top of the stack; top() - Get the top element; getMin() - Retrieve the minimum element in the stack.

1 ≤ number of operations ≤ 10^5-10^9 ≤ x ≤ 10^9pop, top, and getMin operations will always be called on non-empty stacks
Edge cases: Only one element pushed → getMin returns that elementAll elements pushed are the same → getMin returns that elementStrictly increasing elements pushed → getMin returns the first pushed element
</>
IDE
class MinStack: def __init__(self): pass def push(self, x: int) -> None: pass def pop(self) -> None: pass def top(self) -> int: pass def getMin(self) -> int: passpublic class MinStack { public MinStack() {} public void push(int x) {} public void pop() {} public int top() {} public int getMin() {} }class MinStack { public: MinStack() {} void push(int x) {} void pop() {} int top() {} int getMin() {} };class MinStack { constructor() {} push(x) {} pop() {} top() {} getMin() {} }
class MinStack:
    def __init__(self):
        # Write your solution here
        pass
    def push(self, x: int) -> None:
        pass
    def pop(self) -> None:
        pass
    def top(self) -> int:
        pass
    def getMin(self) -> int:
        pass
class MinStack {
    public MinStack() {
        // Write your solution here
    }
    public void push(int x) {
    }
    public void pop() {
    }
    public int top() {
        return 0;
    }
    public int getMin() {
        return 0;
    }
}
#include <vector>
using namespace std;

class MinStack {
public:
    MinStack() {
        // Write your solution here
    }
    void push(int x) {
    }
    void pop() {
    }
    int top() {
        return 0;
    }
    int getMin() {
        return 0;
    }
};
class MinStack {
    constructor() {
        // Write your solution here
    }
    push(x) {
    }
    pop() {
    }
    top() {
        return null;
    }
    getMin() {
        return null;
    }
}
Coming soon
0/9
Common Bugs to Avoid
Wrong: getMin returns wrong minimum after popNot popping from auxiliary min stack when popping main stack✅ Pop from min stack in pop() method whenever popping main stack
Wrong: getMin returns incorrect value with duplicate minimumsNot pushing duplicates of minimum value onto min stack✅ Push min(minStack.top(), x) on every push, even if equal
Wrong: getMin returns wrong after popping all elements and pushing newMin stack not reset after emptying main stack✅ Initialize min stack on first push after empty stack
Wrong: TLE on large inputgetMin implemented by scanning entire stack each time✅ Maintain auxiliary min stack to get min in O(1)
✓
Test Cases
t1_01basic
Input["push(-2)","push(0)","push(-3)","getMin()","pop()","top()","getMin()"]
Expected[null,null,null,-3,null,0,-2]

After pushing -2, 0, -3, the minimum is -3. After popping -3, top is 0 and minimum is -2.

t1_02basic
Input["push(1)","push(2)","push(-1)","getMin()","pop()","getMin()","top()"]
Expected[null,null,null,-1,null,1,1]

Minimum after pushing 1,2,-1 is -1. After popping -1, minimum is 1 and top is 1.

t2_01edge
Input["push(42)","getMin()","top()"]
Expected[null,42,42]

Single element pushed; getMin and top both return that element.

t2_02edge
Input["push(5)","push(5)","push(5)","getMin()","pop()","getMin()","pop()","getMin()","top()"]
Expected[null,null,null,5,null,5,null,5,5]

All elements identical; getMin always returns that element even after pops.

t2_03edge
Input["push(1)","push(2)","push(3)","getMin()","pop()","getMin()","pop()","getMin()"]
Expected[null,null,null,1,null,1,null,1]

Strictly increasing elements; minimum remains the first pushed element.

t3_01corner
Input["push(3)","push(2)","push(1)","getMin()","pop()","getMin()","pop()","getMin()"]
Expected[null,null,null,1,null,2,null,3]

Strictly decreasing elements; minimum updates to last pushed element.

t3_02corner
Input["push(2)","push(0)","push(3)","push(0)","getMin()","pop()","getMin()","pop()","getMin()","pop()","getMin()"]
Expected[null,null,null,null,0,null,0,null,0,null,2]

Multiple minimum duplicates pushed; getMin returns correct min after each pop.

t3_03corner
Input["push(1)","push(2)","pop()","pop()","push(3)","getMin()"]
Expected[null,null,null,null,null,3]

Pop all elements then push new element; getMin returns new element correctly.

t4_01performance
Input["push(1000000)","push(999999)","push(999998)","push(999997)","push(999996)","push(999995)","push(999994)","push(999993)","push(999992)","push(999991)","push(999990)","push(999989)","push(999988)","push(999987)","push(999986)","push(999985)","push(999984)","push(999983)","push(999982)","push(999981)","push(999980)","push(999979)","push(999978)","push(999977)","push(999976)","push(999975)","push(999974)","push(999973)","push(999972)","push(999971)","push(999970)","push(999969)","push(999968)","push(999967)","push(999966)","push(999965)","push(999964)","push(999963)","push(999962)","push(999961)","push(999960)","push(999959)","push(999958)","push(999957)","push(999956)","push(999955)","push(999954)","push(999953)","push(999952)","push(999951)","push(999950)","push(999949)","push(999948)","push(999947)","push(999946)","push(999945)","push(999944)","push(999943)","push(999942)","push(999941)","push(999940)","push(999939)","push(999938)","push(999937)","push(999936)","push(999935)","push(999934)","push(999933)","push(999932)","push(999931)","push(999930)","push(999929)","push(999928)","push(999927)","push(999926)","push(999925)","push(999924)","push(999923)","push(999922)","push(999921)","push(999920)","push(999919)","push(999918)","push(999917)","push(999916)","push(999915)","push(999914)","push(999913)","push(999912)","push(999911)","push(999910)","push(999909)","push(999908)","push(999907)","push(999906)","push(999905)","push(999904)","push(999903)","push(999902)","push(999901)"]
⏱ Performance - must finish in 2000ms

n=100 pushes with strictly decreasing values; O(n) time per operation required to avoid TLE.

Practice

(1/5)
1. You need to implement a fixed-size data structure that supports enqueue and dequeue operations in O(1) time, and efficiently reuses space when elements are removed. Which approach best guarantees these requirements?
easy
A. Use a stack and reverse it on each dequeue operation.
B. Use a dynamic array and shift elements on dequeue to maintain order.
C. Use a linked list with pointers to head and tail nodes.
D. Use a fixed-size array with head and tail pointers and a size variable to distinguish full and empty states.

Solution

  1. Step 1: Understand the problem constraints

    The data structure must support enqueue and dequeue in O(1) time and reuse space efficiently without shifting elements.
  2. Step 2: Evaluate each approach

    Dynamic array with shifting causes O(n) dequeue time. Linked list supports O(1) enqueue/dequeue but uses extra memory and is not fixed-size. Stack reversal is inefficient and does not support queue semantics. Fixed-size array with head, tail, and size variables supports O(1) operations and distinguishes full/empty states correctly.
  3. Final Answer:

    Option D -> Option D
  4. Quick Check:

    Fixed-size array with pointers and size variable ensures O(1) enqueue/dequeue [OK]
Hint: Fixed-size array + pointers + size = O(1) queue ops [OK]
Common Mistakes:
  • Thinking shifting elements is efficient
  • Confusing stack with queue
  • Ignoring full vs empty state distinction
2. Suppose you want to extend the circular queue to support a 'peekRear' operation that returns the last inserted element in O(1) time. Which modification to the optimal approach is correct?
hard
A. Return queue[tail] directly, since tail points to the last element inserted.
B. Return queue[(tail - 1 + capacity) % capacity], since tail points to the next insertion position.
C. Return queue[head], because head always points to the rear element.
D. Return queue[size - 1], since size tracks the number of elements.

Solution

  1. Step 1: Understand tail pointer semantics

    Tail points to the next insertion index, not the last inserted element.
  2. Step 2: Calculate rear element index

    The last inserted element is at (tail - 1 + capacity) modulo capacity to handle wrap-around.
  3. Final Answer:

    Option B -> Option B
  4. Quick Check:

    Rear element index is tail - 1 modulo capacity [OK]
Hint: Rear element is at (tail - 1) mod capacity [OK]
Common Mistakes:
  • Using tail index directly
  • Confusing head with rear
  • Using size as index
3. Suppose you want to extend the one-queue stack implementation to support a multiPop(k) operation that pops the top k elements at once. Which modification maintains correct stack order and efficient operations?
hard
A. Perform k consecutive pop() calls on the queue, each O(1), total O(k).
B. Modify push to rotate the queue k times after each push to prepare for multiPop.
C. Use two queues instead, transferring elements to support batch pop efficiently.
D. Store elements in a separate stack data structure internally to support multiPop efficiently.

Solution

  1. Step 1: Understand multiPop operation

    multiPop(k) means popping top k elements in stack order.
  2. Step 2: Analyze one-queue approach

    Since pop is O(1), performing k pops consecutively is O(k) and maintains order.
  3. Step 3: Evaluate other options

    Rotating queue k times after push breaks order; two queues add complexity; using stack internally violates constraints.
  4. Final Answer:

    Option A -> Option A
  5. Quick Check:

    Consecutive pops maintain order and are efficient in one-queue approach [OK]
Hint: multiPop(k) = k consecutive O(1) pops in one-queue stack [OK]
Common Mistakes:
  • Trying to rotate queue multiple times per push unnecessarily
  • Switching to two queues without reason
  • Using stack internally violating problem constraints
4. Suppose the problem is modified so that the input string can contain not only '(' and ')' but also '*' characters, where '*' can represent either '(' or ')' or an empty string. Which approach is best suited to handle this variant efficiently?
hard
A. Use dynamic programming to track valid substring lengths considering '*' as wildcards.
B. Use the two-pass counter approach ignoring '*' characters.
C. Use brute force checking all substrings and validating with '*' replacements.
D. Use a stack-based approach tracking indices and possible '*' substitutions.

Solution

  1. Step 1: Understand the complexity introduced by '*'

    The '*' character can represent multiple possibilities, requiring tracking multiple states.
  2. Step 2: Evaluate approaches for handling wildcards

    Dynamic programming can efficiently track valid substring lengths considering '*' as '(', ')' or empty, unlike counters or naive stack approaches.
  3. Step 3: Rule out brute force due to exponential possibilities

    Brute force checking all substrings with '*' replacements is infeasible for large inputs.
  4. Final Answer:

    Option A -> Option A
  5. Quick Check:

    DP handles wildcard substitutions efficiently [OK]
Hint: DP handles wildcard '*' efficiently [OK]
Common Mistakes:
  • Ignoring '*' in counters
  • Assuming stack alone can handle wildcards
  • Trying brute force for large inputs
5. Suppose the problem changes so that elements in the array can be negative integers. Which modification to the monotonic stack approach is necessary to correctly compute the sum of subarray minimums?
hard
A. Add a sentinel element with value 0 at the end to ensure stack empties correctly
B. No change needed; the existing monotonic stack approach works for negative values
C. Use a different data structure like a balanced BST to handle negative values
D. Modify the comparison to use <= instead of < to handle negative duplicates

Solution

  1. Step 1: Analyze effect of negative values on monotonic stack

    The monotonic stack approach relies on relative ordering, which holds regardless of sign.
  2. Step 2: Confirm no structural changes needed

    Sentinel and strict < comparison still work; negative values do not break the logic.
  3. Final Answer:

    Option B -> Option B
  4. Quick Check:

    Monotonic stack handles negatives without modification [OK]
Hint: Monotonic stack compares values, sign does not affect correctness [OK]
Common Mistakes:
  • Assuming negative values require different data structures
  • Changing comparison operators unnecessarily
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard