Linear Structures
Stacks, queues, and deques
Order of removal is the whole idea
Three ways to say who goes next. Same box of data - but the rule you use to add and remove decides everything from browser undo to how a search explores.
An array lets you touch any slot by index. A stack, queue, and deque give that up on purpose - they restrict where you may add and remove, and in exchange every operation is a cheap . The restriction is the feature: it turns out "always take from the top" and "always take from the front" are exactly the disciplines that undo, scheduling, and graph traversal need.
Three access rules, one picture
Two disciplines: LIFO and FIFO
Each structure exposes a tiny, fixed vocabulary. A stack has push (add on top), pop (remove the top), and peek (look at the top without removing). A queue has enqueue (add at the rear), dequeue (remove from the front), and front (peek at who's next). A deque simply offers both ends. None of them let you reach into the middle - that's the whole point.
| Structure | Add | Remove | Peek | Order | Cost |
|---|---|---|---|---|---|
| Stack | push → top | pop ← top | peek top | LIFO | O(1) |
| Queue | enqueue → rear | dequeue ← front | front | FIFO | O(1) |
| Deque | both ends | both ends | both ends | either | O(1) |
Under the hood you can build any of them two ways. A linked list gives ends for free - splice a node on or off. An array is faster and cache-friendly for a stack (grow and shrink the tail), but a naïve array queue is a trap: removing the front by shifting everything left is . The fix - a circular buffer - is below.
See the disciplines move
Push and pop the stack and watch values enter and leave the same top end. Enqueue and dequeue the queue and watch values enter at the rear but leave from the opposite front end. Then try the balanced-parentheses checker: type brackets and step through as a stack pushes each opener and pops it when its partner closer arrives - the classic proof that a stack is the right tool for matching.
add & remove at the top - same end
size 3 · top 3
front ← … ← rear - enter rear, leave front
size 3 · front 1 · rear 3
Start with an empty stack, then scan left to right.
Why matching needs a stack
The circular buffer: an O(1) queue in a fixed array
In code
A stack is just a list you only touch at the tail. The bracket checker from the demo is eight lines. And in practice you rarely hand-roll a queue - a deque gives at both ends, so it is a stack, a queue, or a deque depending on which methods you call.
def balanced(s):
pairs = {')': '(', ']': '[', '}': '{'}
stack = []
for ch in s:
if ch in '([{': # opener -> push it
stack.append(ch)
elif ch in ')]}': # closer -> must match the top
if not stack or stack.pop() != pairs[ch]:
return False # nothing to match, or wrong pair
return not stack # balanced iff nothing left unclosed
from collections import deque # O(1) at BOTH ends
q = deque()
q.append(x) # enqueue at rear
q.popleft() # dequeue from front -> FIFO (a queue)
q.append(x); q.pop() # push then pop at rear -> LIFO (a stack)Two ways a queue goes wrong
list.pop(0) (or shifting everything left) makes every dequeue - a hot loop silently degrades to . Use a circular buffer or a deque. 2. Overflow & underflow. A fixed-capacity circular buffer must reject enqueue when full and dequeue when empty; forget the checks and you overwrite live data or read a stale slot. Stacks have the same rule - pop on empty is a bug, not a zero.Check yourself
You push 1, then 2, then 3 onto a stack, then pop once. Which value comes out?
Why does a circular buffer make dequeue O(1) instead of O(n)?
Recall: which end(s) does each structure use, and what order does that produce?
Tie the access rule to the ordering it forces. Try to state it, then check.
Lock it in
- A stack, queue, and deque trade random access for a cheap O(1) add and remove; the restriction on where you touch the data is the whole feature.
- Stack = same end (LIFO), queue = opposite ends (FIFO), deque = both ends (either).
- Matching, undo, expression evaluation, and the call stack are all "reverse the most recent thing" - reach for a stack.
- A circular buffer (or a deque) keeps a queue O(1) by advancing an index instead of shifting cells; a plain-array queue degrades to O(n) per dequeue.
Primary source
Where this reappears