Stacks & Queues

LIFO and FIFO — the two access disciplines behind undo, the call stack, BFS, and every task queue you'll ever deploy.

stackqueuedequefundamentals

Less is more

A stack and a queue are restricted collections: they deliberately forbid random access and offer just two or three operations. The restriction is the feature — it makes behavior predictable, implementations trivially fast, and intent obvious in code.

StackQueue
DisciplineLIFO — Last In, First OutFIFO — First In, First Out
AnalogyPlates: add and take from the topA line at a counter: join at the back, served from the front
Operationspush, pop, peekenqueue, dequeue, peek
All operationsO(1)O(1)
Mental model"most recent first" — undo, backtrack"fairness" — process in arrival order

Stacks

Python
# Python — a list IS a stack (append/pop at the end are O(1))
stack = []
stack.append(10)     # push
stack.append(20)
top = stack[-1]      # peek → 20
x = stack.pop()      # pop  → 20
Java
// Java — use ArrayDeque, not the legacy Stack class
Deque<Integer> stack = new ArrayDeque<>();
stack.push(10);
int top = stack.peek();
int x = stack.pop();
C++
// C++
std::stack<int> st;
st.push(10);
int top = st.top();
st.pop();                 // note: pop() returns void in C++

Where stacks already run your life

  • The call stack — you met it in Functions: every call pushes a frame, every return pops one. Recursion (Level 3) is a stack, which is why any recursive algorithm can be rewritten with an explicit one.
  • Undo / redo — every action pushes onto the undo stack; Ctrl-Z pops it (and pushes onto redo).
  • Browser back button — pages visited, most recent first.
  • Parsing — matching brackets, evaluating expressions, your compiler.

The interview classic: matched brackets

"Is {[()]} valid?" — opens push, closes must match the top:

Python
def is_valid(s):
    pairs = {")": "(", "]": "[", "}": "{"}
    stack = []
    for ch in s:
        if ch in "([{":
            stack.append(ch)
        elif not stack or stack.pop() != pairs[ch]:
            return False
    return not stack          # leftovers = unclosed opens

Why a stack? Because the most recently opened bracket must close first — the problem statement contains the letters L-I-F-O if you squint. Training yourself to hear "most recent first" → stack is the actual lesson.

One level up sits the monotonic stack — keep the stack sorted by popping everything your new element beats. It turns "next greater element" and "daily temperatures" style problems from O(n²) scans into one O(n) pass. Meet it properly in the problem bank.

Watch a stack work

Run a push/pop sequence — note who leaves first. Then edit the operations and try to predict the final state before pressing play.

Stack (LIFO)time O(1) per opspace O(n)
top
(empty)
bottom

1/11Empty stack. push adds to the top; pop removes from the top (LIFO).

Queues

Python
# Python — NOT a list! Use deque (list.pop(0) shifts everything: O(n))
from collections import deque
q = deque()
q.append(10)         # enqueue at the back
q.append(20)
front = q[0]         # peek → 10
x = q.popleft()      # dequeue from the front → 10, in O(1)
Java
// Java
Queue<Integer> q = new ArrayDeque<>();
q.offer(10);
int x = q.poll();
C++
// C++
std::queue<int> q;
q.push(10);
int x = q.front(); q.pop();

Where queues run the world

  • BFS — breadth-first search: explore a graph level by level; the queue holds the frontier. This is the algorithmic reason queues exist, and it's how shortest paths in unweighted graphs are found.
  • Task/message queues: printers, web servers under load, and the industrial-strength version — Kafka/SQS pipelines (messaging & queues) — are all "absorb requests at the back, process from the front." The humble deque here and Level 6's distributed queue are the same idea at different scales.
  • Rate limiting & buffering: smooth a bursty producer feeding a steady consumer (rate limiting).

The deque — both ends at once

A deque (double-ended queue, "deck") allows O(1) push/pop at both ends — it's the general tool that plays stack and queue. Its star solo is the sliding-window maximum problem, where it powers an O(n) solution (sliding window).

Watch a queue work

Same operations, opposite exit door — the oldest element leaves first.

Queue (FIFO)time O(1) per opspace O(n)
front →
(empty)
← back

1/11Empty queue. enqueue adds at the back; dequeue removes from the front (FIFO).

Build one from the other (interview favorite)

Queue from two stacks: push into inbox; to dequeue, if outbox is empty, pour inbox into it (reversing order), then pop outbox:

Python
class QueueFromStacks:
    def __init__(self):
        self.inbox, self.outbox = [], []

    def enqueue(self, x):
        self.inbox.append(x)

    def dequeue(self):
        if not self.outbox:
            while self.inbox:
                self.outbox.append(self.inbox.pop())   # reverse the order
        return self.outbox.pop()

Each element is moved at most twice ever → amortized O(1) per operation (the arrays page accounting again). The mirror problem — stack from queues — exists too; one of the pair shows up constantly in first rounds.

Production perspective

  • Stacks and queues are usually built on arrays or linked lists — they're disciplines, not layouts. Array-backed (with the resize-doubling trick) is the practical default.
  • The names persist up the stack: "stack trace," "task queue," "event queue" (the heart of Node.js — Level 7), "undo stack." When you read infrastructure docs, these aren't metaphors — there is literally a queue in there.
  • Choosing LIFO vs FIFO is a product decision sometimes: a job scheduler that LIFOs feels snappy for new requests but starves old ones — fairness vs recency, in one letter.

Common mistakes

  • list.pop(0) as dequeue in Python — shifts every element, O(n); a loop of them is O(n²). deque.popleft() exists for exactly this.
  • Peeking/popping an empty stack — crash (exceptions). The not stack guard in is_valid above is half the solution's correctness.
  • Using Java's Stack class — legacy, synchronized, slow; ArrayDeque is the documented replacement.
  • Forgetting the leftover check — bracket-matching that returns True with "(((" unprocessed. The final return not stack matters.
  • Hearing "queue" and reaching for sorting — FIFO already is the order; if you need priority order instead, that's a heap, two pages from now.

Think it through

The page teased the monotonic stack — now derive it. This is the canonical "turn an O(n²) re-scan into one O(n) pass with a stack" reasoning, and once it clicks you own a whole family of problems.

Think it through: Daily TemperaturesMedium — LeetCode 7390/5 stages

PROBLEMtemps = [73,74,75,71,69,72,76,73]. For each day, how many days until a WARMER temperature? Use 0 if none ever comes. Answer: [1,1,4,2,1,1,0,0].

  1. 1

    Restate & edges

    For day i, what exactly am I looking for?

  2. 2

    Brute force first

    Obvious solution and its cost?

    unlocks after the stage above
  3. 3

    Find the pattern

    As I walk left to right, which past days are still 'waiting', and who resolves them?

    unlocks after the stage above
  4. 4

    Code the template

    Push indices (not temps) — why indices?

    unlocks after the stage above
  5. 5

    Cost & edge check

    Why is this O(n) even with a while-loop inside the for-loop?

    unlocks after the stage above

Check yourself

Check yourself0/4 answered

1. In bracket matching, which input is the reason you must check `not stack` BEFORE popping?

2. Why is `list.pop(0)` the wrong way to dequeue in Python?

3. BFS uses a queue, DFS uses a stack. Swap the container in the same traversal loop and you get:

4. Queue-from-two-stacks: one dequeue can cost O(n), yet we call it amortized O(1). Why?

Interview perspective

Practice — climb the ladder

If the problem whispers "most recent first" it is a stack; "first come, first served" is a queue; "next greater/smaller" is a monotonic stack.

Practice ladder: Stacks & Queues0/8 solved

Climb in order — every rung assumes the one above it. Solve on LeetCode, then tick it here; progress is saved on this device.

Warm-up

LIFO/FIFO mechanics
  1. The canonical stack problem — nesting means LIFO, with three distinct failure modes.

  2. Two stacks make a queue — amortized analysis you can explain out loud.

Core

stacks carrying extra state
  1. Min StackMedium

    Auxiliary stack tracking the running minimum — state rides along with each push.

  2. Monotonic stack — indices wait on the stack until a warmer day pops them.

  3. Monotonic stack + hash map — the same pattern in its simplest costume.

  4. Stack as evaluator — how compilers and calculators actually run expressions.

Stretch

the two famous hard ones
  1. Monotonic stack at full power — each bar pops exactly once; O(n) where brute force is O(n²).

  2. Monotonic DEQUE — front holds the answer, back evicts the obsolete.