DAY 17 / 30 · WEEK 3

Stacks

Goal of the day

Concept

A stack is a collection where you can only add or remove from one end — the top. Think of a stack of plates: you place a new plate on top (push), and when you need one, you take the top plate off (pop) — you never pull a plate from the middle or bottom without first removing everything above it. This ordering is called LIFO: Last In, First Out. It's the opposite of a queue (FIFO — First In, First Out), which we'll build on Day 18.

A stack supports exactly three operations, all O(1):

In JavaScript, a plain array already behaves like a stack if you only ever use .push() and .pop() — both operate on the end of the array, which we simply treat as "the top." No special class is required.

Stacks show up whenever a problem needs to undo or match against "whatever happened most recently" — undo history in an editor, the back button in a browser, and, today's core example, matching brackets: when a closing bracket arrives, it must match the most recently opened, still-unclosed bracket — exactly what a stack's top gives you for free.

Diagram: push and pop always happen at the top

Push '{', '[', '(' — newest stays on top (scanning the start of "{[()()]}") '(' — top of stack '[' '{' The next incoming character must match the TOP frame first — '(' — not '[' or '{'. ')' arrives → matches '(' → pop the '(' frame is removed '[' — new top '{' '(' is gone — it matched and popped. Next incoming closer must match '['. Push adds to the top; pop removes from the top — always LIFO. A stack never touches the middle or bottom.

Diagram: Min Stack — track the running minimum alongside each value

After push(5), push(3), push(7), push(1): value 1 | running min 1 value 7 | running min 3 value 3 | running min 3 value 5 | running min 5 getMin() just reads the top frame's stored min — O(1), no scanning. Pop 1, and 7's own stored min (3) is instantly correct.

Interactive visualization: Valid Parentheses, character by character

reading this character top of stack comparing top vs. incoming closer matched / valid mismatch / invalid
Press Play or Step to begin.

Interactive visualization: Min Stack — push/pop with running min

top of stack lower frames
Press Play or Step to begin.

The code: Valid Parentheses

function isValid(s) {
  const stack = [];
  const pairs = { ')': '(', ']': '[', '}': '{' }; // closer -> the opener it must match

  for (const ch of s) {
    if (ch === '(' || ch === '[' || ch === '{') {
      stack.push(ch);                 // opener — push it, we'll need it later
    } else {
      // closer — first make sure there's something to match against
      if (stack.length === 0 || stack.pop() !== pairs[ch]) return false;
    }
  }
  return stack.length === 0; // true only if every opener got matched and popped
}

Line by line:

Dry run: isValid("{[()()]}")

IndexCharActionStack (bottom → top)
0{opener — push{
1[opener — push{ [
2(opener — push{ [ (
3)top is '(' — matches → pop{ [
4(opener — push{ [ (
5)top is '(' — matches → pop{ [
6]top is '[' — matches → pop{
7}top is '{' — matches → pop(empty)
end of string, stack is emptyreturn true

Every closer matched the top of the stack at the moment it arrived, and the stack was back to empty by the end — that combination is exactly what "valid" means.

Complexity

ApproachTimeSpaceWhy
Valid ParenthesesO(n)O(n)Each character is pushed or popped at most once (a single pass). Worst case — all openers, e.g. "(((((" — every character sits on the stack at once.
Min Stack (push, pop, top, getMin)O(1) eachO(n)Every operation only touches the top of one or two arrays — no loop, no scan. The space cost is a second array the same size as the first, to store each level's running min.

Interview corner

How to talk through it out loud: name the invariant before writing code — "I'll use a stack because the most recently opened bracket has to close first, that's LIFO." Then narrate each branch as you type it: "opener → push it. Closer → first check the stack isn't empty, then check the top matches. At the very end, it's only valid if the stack is also empty." Interviewers are listening for you to say the empty-stack guard and the final-emptiness check out loud, not just for code that happens to pass the example they gave you.

Practice problems

1. Valid Parentheses (Easy — LeetCode: Valid Parentheses)

Given a string containing just ()[]{}", determine if the input string is valid — today's core example, worked through above.

2. Min Stack (Medium — LeetCode: Min Stack)

Design a stack that supports push, pop, top, and retrieving the minimum element, all in O(1) time.

3. Daily Temperatures (Medium — LeetCode: Daily Temperatures)

Given daily temperatures, return an array where answer[i] is the number of days until a warmer temperature (0 if there isn't one).

Quiz

1. What ordering principle describes a stack?

2. In Valid Parentheses, what must you check before popping the stack when a closing bracket arrives?

3. Why must you check the stack is empty again AFTER the scan finishes, not just during it?

4. What are the time and space complexity of Valid Parentheses?

5. How does Min Stack achieve O(1) getMin()?