Stacks and the last-in first-out rule

One end, three operations, and a surprising number of problems that dissolve because of it.

A stack allows access at one end only: push, pop, peek. Everything is O(1), and an array is a perfectly good implementation.

const stack = [];
stack.push('a');
stack.push('b');
stack.pop();       // 'b' — the last one in
stack.at(-1);      // 'a' — peek without removing

Why the restriction helps

Anything with nesting is a stack problem, because nesting is last-in-first-out by nature: the most recently opened thing must close first.

function isBalanced(text) {
  const pairs = { ')': '(', ']': '[', '}': '{' };
  const stack = [];

  for (const ch of text) {
    if (ch =<mark> '(' || ch </mark>= '[' || ch === '{') stack.push(ch);
    else if (ch in pairs) {
      if (stack.pop() !== pairs[ch]) return false;   // wrong closer, or empty
    }
  }

  return stack.length === 0;    // nothing left open
}

Note both failure modes: a mismatched closer, and unclosed openers at the end. Forgetting the second is the usual bug.

Where stacks are already working for you

  • The call stack. Function calls are pushed and popped; recursion is a stack you did not write.
  • Undo history. The last action reverses first.
  • Expression evaluation. Infix to postfix conversion, and evaluating the postfix, are both stack algorithms.
  • Iterative DFS. Replace the recursion's implicit stack with an explicit one.
  • Backtracking. Choose, explore, un-choose is push, recurse, pop.

The min stack

A common follow-up: support getMin() in O(1) alongside push and pop. You cannot scan — that is O(n). Keep a second stack of minima:

class MinStack {
  #values = [];
  #mins = [];

  push(v) {
    this.#values.push(v);
    this.#mins.push(this.#mins.length ? Math.min(v, this.at(-1)) : v);
  }

  pop() { this.#mins.pop(); return this.#values.pop(); }
  at(i) { return this.#mins.at(i); }
  getMin() { return this.#mins.at(-1); }
}

Each entry records the minimum as of that moment, so popping restores the previous minimum automatically. O(1) everywhere, O(n) extra space — and the same "carry the answer alongside the data" idea reappears in the monotonic stack next.