Last In, First Out (LIFO)
CS205 Data Structures
Use arrow keys or buttons to navigate • Press S to reveal steps
A stack is a collection of elements with a LIFO access policy:
Think of a spring-loaded plate dispenser in a cafeteria. You always take the top plate. When clean plates are added, they go on top. You never pull from the bottom.
LIFO = Last In, First Out. The most recently added element is always the next one to leave.
| Method | Description | Time |
|---|---|---|
push(e) | Add element e to top | O(1) |
pop() | Remove & return top | O(1) |
top() / peek() | Return top without removing | O(1) |
isEmpty() | Is the stack empty? | O(1) |
size() | Number of elements | O(1) |
Every operation is O(1) -- constant time. A stack does very little, but does it blazing fast.
pop() and top() on an empty stack should throw an EmptyStackException (or return null, depending on design).
Add an element to the top of the stack
push(D)Push is always O(1). It never needs to shift or move existing elements.
Remove and return the top element
pop() returns DCalling pop() on an empty stack is a runtime error. Always guard with isEmpty() or handle the exception.
Pop is O(1). Setting data[t] = null in the array version avoids memory leaks by allowing garbage collection.
Look at the top element without removing it
peek() is like looking at the top plate without picking it up. pop() is actually taking it.
| Aspect | top() | pop() |
|---|---|---|
| Returns top? | Yes | Yes |
| Removes top? | No | Yes |
| Changes size? | No | Yes (-1) |
| Destructive? | No | Yes |
Store elements in an array, track the top index
The stack "grows right" in the array. Index 0 is the bottom, index t is the top. An empty stack has t = -1.
What happens when we push onto a full array?
Simple but limiting. Caller must know the max size in advance.
Doubling seems expensive (O(n) copy), but it happens rarely.
Total cost of n pushes is O(n), so each push is O(1) amortized. The occasional expensive resize is "paid for" by all the cheap pushes that preceded it.
Never grow by a constant (e.g. +10). That gives O(n) amortized per push. Always double for O(1) amortized.
Push and pop at the head of a singly linked list
Push/pop at the head of a singly linked list is O(1). No resizing needed. No wasted capacity. No fixed limit.
Tradeoffs between the two approaches
| Criterion | Array-Based | Linked-List-Based |
|---|---|---|
| push / pop | O(1) amortized | O(1) worst-case |
| Memory per element | Low -- just the element | Higher -- element + pointer |
| Wasted space | Up to N unused slots | None |
| Resize cost | O(n) occasionally | Never needed |
| Maximum size | Fixed (unless dynamic) | Limited only by memory |
| Cache performance | Excellent -- contiguous | Poor -- scattered nodes |
| Implementation | Simpler | Slightly more complex |
When you know the maximum size in advance, or need best cache performance. Most practical implementations use arrays (e.g., java.util.Stack).
When the stack size is highly unpredictable, or you need guaranteed O(1) worst-case per operation (no amortized spikes).
Check if ( { [ ] } ) is balanced using a stack
( [ { : push it) ] } : pop and check match( { [ ] } )( [ ) ]Evaluate 3 4 + 2 * using a stack
Operators come after their operands. No parentheses needed!
| Infix | Postfix |
|---|---|
3 + 4 | 3 4 + |
(3 + 4) * 2 | 3 4 + 2 * |
3 + 4 * 2 | 3 4 2 * + |
3 4 + 2 *Shunting-Yard Algorithm (Dijkstra)
( : push onto operator stack) : pop and output until ( is foundThe operator stack holds operators "waiting" for their right operand. Higher-precedence operators are flushed first, ensuring correct evaluation order.
3 + 4 * 23 4 2 * +How your computer manages function calls with a stack
Every time a function is called, a new stack frame is pushed. When the function returns, its frame is popped.
Recursion is just the call stack in action. Each recursive call pushes a frame. Deep recursion = tall stack.
factorial(3)Two stacks power every editor's undo system
A new action clears the Redo stack. Once you do something new after undoing, you lose the redo history.
Navigation history uses two stacks
Same pattern as Undo/Redo! The Back stack is the Undo stack, the Forward stack is the Redo stack, and the current page is the document state.
Mistakes to avoid when working with stacks
The #1 stack bug. Always check isEmpty() before pop() or top().
Deep recursion = deep call stack. Each call pushes a frame. Too deep and you get StackOverflowError.
In postfix evaluation, operand order matters for - and /.
First popped = second operand. Second popped = first operand.
| Operation | Time | Notes |
|---|---|---|
push(e) | O(1)* | Add to top |
pop() | O(1) | Remove & return top |
top() | O(1) | Return top (no remove) |
isEmpty() | O(1) | Check if empty |
size() | O(1) | Element count |
* O(1) amortized for dynamic array
Simple, fast, great cache performance. Use when size is bounded or mostly known. Amortized O(1) with doubling.
No size limit, guaranteed O(1) worst-case. Extra memory for node pointers.
| Application | How Stacks Help |
|---|---|
| Parenthesis matching | Push open, pop on close |
| Postfix evaluation | Push nums, pop for ops |
| Infix to postfix | Operator stack (shunting-yard) |
| Function calls | Call stack / recursion |
| Undo / Redo | Two stacks |
| Browser history | Back & Forward stacks |
A stack is a restricted list where you can only touch the top -- and that restriction is what makes it powerful, fast, and useful everywhere.