Stacks
Core idea
A stack follows LIFO — Last In, First Out.
┌───────┐
│ 30 │ ← top / first out
├───────┤
│ 20 │
├───────┤
│ 10 │
└───────┘
Operations
- Push — add to the top
- Pop — remove and return the top
- Peek/Top — inspect the top without removing
- Is Empty — check whether the stack is empty
Example:
stack = []
stack.append(10)
stack.append(20)
stack.append(30)
stack.pop() # 30
stack[-1] # 20
Array-based implementation
Python’s list is a dynamic array, so use its end as the stack top.
stack.append(x) # push
stack.pop() # pop
stack[-1] # peek
Do not use:
stack.insert(0, x)
stack.pop(0)
because removing from index 0 requires shifting elements → O(n).
Complexity
| Operation | Complexity |
|---|---|
Push / append() |
O(1) amortized |
Pop / pop() |
O(1) |
Peek / stack[-1] |
O(1) |
| Is empty | O(1) |
| Search | O(n) |
Push has:
Normal push → O(1)
Resize push → O(n) worst case
Amortized → O(1)
Amortized O(1) does not mean every push is O(1).
Underflow and overflow
Underflow: attempting to pop from an empty stack.
Overflow: attempting to push beyond available capacity; mainly relevant to fixed-size stacks or call stacks.
Common applications
Function call stack
Function calls follow LIFO:
main()
↓
foo()
↓
bar()
bar() finishes first.
Bracket matching
Stacks are useful for nested brackets:
({[]})
Push opening brackets; when a closing bracket appears, check the most recent opening bracket.
Undo/redo
The most recent action is undone first.
Reversing data
stack = []
for x in ["A", "B", "C", "D"]:
stack.append(x)
while stack:
print(stack.pop())
Output:
D
C
B
A
Stack vs Queue
Stack → LIFO
Queue → FIFO
Mental model:
Stack → pile of plates
Queue → line of people
Interview pattern
When you see:
most recent
last
undo
nested
matching brackets
reverse
backtracking
consider whether a stack is appropriate.
Simple implementation
class Stack:
def __init__(self):
self.stack = []
def push(self, value):
self.stack.append(value)
def pop(self):
if not self.stack:
return None
return self.stack.pop()
def peek(self):
if not self.stack:
return None
return self.stack[-1]
def is_empty(self):
return len(self.stack) == 0
Interview answers
What is a stack?
A data structure that follows LIFO: the last element inserted is the first element removed.
Why is pop O(1)?
When the top is maintained at the end of an array, removing the last element does not require shifting the remaining elements.
Why is push amortized O(1)?
A dynamic array occasionally resizes in O(n), but resizing is infrequent, so the total cost across many pushes gives O(1) amortized cost.
Why not pop(0)?
Removing the first array element requires shifting the remaining elements, making it O(n).
Quick revision
STACK
LIFO = Last In, First Out
push → O(1) amortized
pop → O(1)
peek → O(1)
empty → O(1)
search → O(n)
Python:
append() → push
pop() → pop
[-1] → peek
Avoid pop(0) for stacks → O(n)
Common uses:
- Call stack
- Bracket matching
- Undo/redo
- Reversal
- Backtracking