If arrays are about storing data, stacks are about controlling the order you process it. They're one of the simplest data structures — but they show up in surprisingly many interview problems.
This post breaks down what stacks are, how they work under the hood, and why the LIFO constraint is so powerful.
What is a stack?
A stack is a collection where the last element added is the first one removed.
This is called LIFO — Last In, First Out.
You interact with it through one end only: the top.
Some everyday examples:
A stack of plates — you add to the top, you take from the top
The browser back button — most recently visited page comes off first
Ctrl+Z (undo) — the most recent action is undone first
That's it. Push, pop, and peek. Three operations.
A stack is just a restricted array
Under the hood, most stack implementations use a dynamic array.
The difference? A stack deliberately limits what you can do:
You can only add to the end (push)
You can only remove from the end (pop)
You can only look at the end (peek)
This is the same array you already know — but with rules.
pythonA Python list IS a stack:
1stack = []2stack.append(1) # push3stack.append(2) # push4stack.append(3) # push5stack.pop() # → 3 (last in, first out)6stack[-1] # → 2 (peek)
You could also implement a stack with a linked list, but in practice — especially in interviews — a dynamic array is the standard approach.
Push is O(1)
Adding an element to the top of the stack is constant time.
Why? Because it's the same operation as appending to an array — write into the next available slot.
One thing to watch: popping from an empty stack raises an error in most languages. Always check before you pop.
Peek is O(1)
Looking at the top element without removing it is also constant time.
pythonConstant-time peek:
1stack[-1] # → 10, O(1)
In interviews, you'll peek constantly — to check the top value before deciding whether to push or pop.
There is no random access
You cannot access the 3rd element of a stack. You cannot iterate from the bottom. You cannot index into it.
That's the point.
A stack forces you to process elements in reverse order of insertion. This constraint is what makes it useful — it eliminates ambiguity about which element to process next.
The answer is always: the most recent one.
Searching a stack is O(n)
If you need to check whether a value exists in a stack, you'd have to pop elements one by one and examine each.
That's O(n) time — and it destroys the stack in the process.
If you're searching, you probably want a different data structure. Stacks are designed for ordered processing, not lookup.
The call stack — recursion is literally a stack
Every time your program calls a function, it pushes a frame onto the call stack. Every time a function returns, it pops that frame off.
This is why:
Recursive functions can overflow the stack (too many pushes, not enough pops)
You can always convert recursion to an explicit stack
DFS uses a stack — whether you write it recursively or iteratively
pythonRecursive DFS vs iterative DFS — same stack:
1# Recursive (implicit stack)2def dfs(node):3 if not node:4 return5 dfs(node.left)6 dfs(node.right)78# Iterative (explicit stack)9def dfs(root):10 stack = [root]11 while stack:12 node = stack.pop()13 if node.right:14 stack.append(node.right)15 if node.left:16 stack.append(node.left)
Understanding that recursion IS a stack is one of the most important mental models in DSA.
When a problem is secretly about stacks
Stacks don't always announce themselves. Here are the signals:
"Most recent" or "last seen" — if the problem cares about the most recent element, that's LIFO.
"Match" or "balance" — matching brackets, tags, or pairs. Push the opener, pop when you see the closer.
"Reverse order" — if you need to process something in the opposite order it was received.
"Next greater" or "next smaller" — this leads to the monotonic stack pattern, one of the most common stack-based interview patterns.
"Undo" or "backtrack" — if the problem involves reverting to a previous state.
When you recognize these cues, the stack becomes obvious.
Know the common stack operations in your language
For Python (using a list as a stack):
stack.append(x) → O(1) amortized (push)
stack.pop() → O(1) (pop)
stack[-1] → O(1) (peek)
len(stack) → O(1) (size)
len(stack) == 0 → O(1) (is empty)
There's no dedicated Stack class in Python. A list is the standard approach. You can also use collections.deque for the same operations with guaranteed O(1) performance (no amortization).
Knowing these costs lets you reason about performance before you write code.
Final takeaway
Stacks are simple — push, pop, peek — but that simplicity is the strength.
Understanding:
why all operations are O(1)
that a stack is just a restricted array
that recursion literally IS a stack
the interview signals that point to a stack solution
will make you faster at recognizing stack problems and cleaner at implementing solutions. When the problem cares about the most recent element — reach for a stack.