Stacks vs Queues: LIFO and FIFO Data Structures
Stacks remove the most recent item first (LIFO); queues remove the oldest first (FIFO). How each works, their operations, and where they show up.
A stack is a data structure that removes the most recently added item first — last in, first out, or LIFO — while a queue removes the oldest item first — first in, first out, or FIFO. Both hold an ordered collection of elements, and both deliberately restrict how you can access them: a stack only lets you touch the top, a queue only lets you add at the back and remove from the front. That restriction is the whole point — it’s what makes certain algorithms simple to reason about and certain operations fast.
Core operations
A stack supports three operations, each running in constant time, O(1) — see Big O notation for what that means in practice:
- push — add an item to the top
- pop — remove and return the top item
- peek — look at the top item without removing it
A queue supports the equivalent trio — enqueue (add to the back), dequeue (remove from the front), and peek (look at the front) — also O(1), but only with the right underlying implementation. A queue built naively on top of an array, where dequeuing means shifting every remaining element down one slot, degrades to O(n) per removal. A proper queue implementation uses a linked list or a circular buffer, so both ends can be touched in constant time without shuffling the rest of the collection.
Stacks in the wild
- The call stack. Every time a function calls another function, the runtime pushes a new frame onto a call stack; when a function returns, its frame pops off. This is the mechanism behind recursion, and it’s also why unbounded recursion eventually throws a stack overflow — the structure has a finite size. It’s a different structure from the task queue that governs the JavaScript event loop, which schedules callbacks rather than tracking active function calls.
- Undo/redo history in editors — each action pushes onto an undo stack; undoing pops it off and pushes onto a redo stack.
- Browser back-button history — visiting a new page pushes it on; back pops the most recent one off.
- Expression parsing and bracket matching — a compiler or linter tracking
(,[,{pushes each opening bracket and pops on the matching close, flagging a mismatch if the stack empties too soon or isn’t empty at the end.
Queues in the wild
- Task scheduling — jobs processed in the order they arrived, fairly, without newer work jumping ahead of older work.
- Breadth-first search (BFS), which explores a graph level by level using a queue — see BFS vs DFS for how that compares to the stack-based (or recursive) approach DFS takes to the same graph.
- Distributed work queues — the same FIFO idea scaled up to a whole system, where producers and consumers run independently; see what a message queue is for how that plays out across services rather than within a single program.
Variants worth knowing
Two extensions come up often enough to name separately:
- Deque (double-ended queue). Supports push and pop at both ends, so it can act as a stack, a queue, or both at once. Useful for sliding-window algorithms that need to drop items from either side.
- Priority queue. Instead of serving items in insertion order, it always serves the highest (or lowest) priority item next, regardless of when it arrived. Priority queues are almost always implemented on top of a heap, which keeps both insertion and extraction at O(log n) rather than requiring a full sort on every operation.
Comparison table
| Stack | Queue | |
|---|---|---|
| Order | LIFO — last in, first out | FIFO — first in, first out |
| Add operation | push (to the top) | enqueue (to the back) |
| Remove operation | pop (from the top) | dequeue (from the front) |
| Typical use cases | Recursion, undo history, bracket matching, DFS | Task scheduling, BFS, distributed work queues |
| Efficient implementations | Array or linked list | Linked list or circular buffer |
The takeaway
Stacks and queues are both ordered collections with one operation removed on purpose — a stack forgets everything except “what’s on top,” a queue forgets everything except “what’s oldest.” That narrow interface is a feature: it’s what lets a call stack unwind function calls correctly, what lets BFS explore a graph level by level, and what lets a distributed queue process work fairly. When you’re choosing between them, ask which order actually matters for the problem — most recent first, or oldest first — and the right structure usually falls out immediately.
Keep reading
The Lycoris Team · · 4 min read The KMP Algorithm: Fast String Matching Explained
The Knuth-Morris-Pratt algorithm finds a pattern inside a text in linear time by never re-examining characters it has already matched.
The Lycoris Team · · 5 min read What Is a Ring Buffer?
A ring buffer is a fixed-size array that wraps its read and write pointers around, giving O(1) enqueue and dequeue without ever resizing.
The Lycoris Team · · 5 min read Fenwick Trees (Binary Indexed Trees), Explained
A Fenwick tree, or binary indexed tree, answers prefix-sum queries and point updates in O(log n) with far less memory than a segment tree.