Stacks in Python: Easy Guide with Code & Use Cases
Have you ever wondered how the Undo button works? Or how your browser’s Back button remembers the last page?
It’s all thanks to stacks—one of the most straightforward yet powerful data structures in programming.
Think of it like a stack of books: you add one on top, and you grab the last one you placed when you need one. That’s LIFO (Last In, First Out) in action!
In this guide, we’ll cut through the fluff and show you exactly how stacks work in Python. You’ll learn how to implement them efficiently, where they’re used in the real world, and why they’re a must-know for developers.
Ready to stack up your coding skills? Let’s dive in!
Stack Operations & Their Complexity
Stacks are built for speed. No searching. No shifting. Just quick access to the topmost item.
Every operation—adding, removing, or peeking—happens in O(1) time. That means it doesn’t matter if your stack holds 10 elements or 10 million—it’s always lightning-fast.
Here’s how each operation works and why it’s so powerful.
Push (Insert element at the top)
Imagine stacking plates in your kitchen. You place one on top, then another, and another. That’s Push—it simply adds an element to the top of the stack in O(1) time. No shifting, no waiting.
Pop (Remove the top element)
What’s the fastest way to grab a plate? Take the one on top. That’s Pop—it removes the last-added element instantly. O(1) time, no delays.
Peek (View the top element without removing it)
Want to know what’s on top without touching the stack? Peek lets you check the top element without popping it off. Just a quick look before you leap—and yes, it’s also O(1) time.
isEmpty (Check if the stack is empty)
Have you ever reached for a plate, realizing the stack is empty? That’s why isEmpty exists. It tells you if your stack has anything left before you try to remove something that isn’t there. And, of course, it runs in O(1) time.
Size (Get the number of elements in the stack)
Want to know how high your stack is? The Size operation instantly gives you an exact count—no looping or calculations. Just O(1) efficiency at its best.
Stacks are simple, but they’re everywhere—function calls, browser history, undo actions, and more. Here is a summary of time and space complexity
Operation | Time Complexity |
Push | O(1) |
Pop | O(1) |
Peek | O(1) |
isEmpty | O(1) |
Size | O(1) |
Why O(n) Space Complexity?
- A stack needs space for every element stored, so if there are n elements, it takes O(n) space.
- Even though each operation only modifies one element at a time, the total memory usage grows with the stack size.
This makes stacks fast but memory-dependent—perfect for scenarios where quick access matters more than storage!
Now, let’s build one in Python!
Stack Using Python List (Simplest Approach)
Python’s built-in list makes it easy to create a stack. It supports append() (to push) and pop() (to remove), handling elements just like a real stack—fast and straightforward. But is it the best choice? Let’s break it down.
Why Lists Can Be Used as a Stack
Lists are dynamic arrays, meaning they can grow as needed. The append() method adds elements to the end in O(1) time, while pop() removes them just as quickly.
However, lists weren’t designed as stacks. If you remove elements from the front (pop(0)), every element shifts left—O(n) time. This is where better alternatives come in.
Python List Stack Implementation
Here’s how you can create a basic stack using a list:
class Stack:
def __init__(self):
self.stack = []
def push(self, item):
self.stack.append(item) # O(1) operation
def pop(self):
return self.stack.pop() if self.stack else "Stack is empty"
def peek(self):
return self.stack[-1] if self.stack else "Stack is empty"
def is_empty(self):
return len(self.stack) == 0
def size(self):
return len(self.stack)
# Example Usage
stack = Stack()
stack.push(10)
stack.push(20)
print(stack.pop()) # Output: 20
print(stack.peek()) # Output: 10
It works, but is it the best option?
Advantages & Limitations of Using a List
Advantages:
- Simple & built-in—no extra imports needed.
- Fast O(1) operations for push/pop from the end.
Limitations:
- Inefficient for large stacks—lists may need to be resized, causing overhead.
- Slow pop(0) operations—removing from the front is O(n) due to shifting elements.
If you need a faster, memory-efficient stack, there’s a better way. Enter deque.
Stack Using collections.deque (Optimized Approach)
collections.deque (double-ended queue) is designed for fast insertions and deletions from both ends, making it a better alternative to lists for stacks.
Why deque is Better for Stacks
Unlike lists, deque doesn’t need to resize when growing. It’s pop() and append() are always O(1), no matter how large the stack gets. It also allows fast popping from both ends, making it more versatile.
Python Stack Implementation Using deque
from collections import deque
class Stack:
def __init__(self):
self.stack = deque()
def push(self, item):
self.stack.append(item) # O(1) operation
def pop(self):
return self.stack.pop() if self.stack else "Stack is empty"
def peek(self):
return self.stack[-1] if self.stack else "Stack is empty"
def is_empty(self):
return len(self.stack) == 0
def size(self):
return len(self.stack)
# Example Usage
stack = Stack()
stack.push(10)
stack.push(20)
print(stack.pop()) # Output: 20
print(stack.peek()) # Output: 10
Performance Benefits of deque
Why it’s better than a list:
- No resizing overhead—memory-efficient for large stacks.
- True O(1) operations—no slowdowns due to shifting.
If you need a fast, reliable stack, deque is the way to go.
But what if you want to avoid arrays entirely and build a stack from scratch? Let’s talk linked lists.
Stack Using Linked List (Advanced Approach)
Lists and deques are array-based stacks, but a linked list stack uses nodes instead. Each element is stored separately in memory, allowing dynamic growth without resizing issues.
How a Stack Works with Linked Lists
Instead of using an array, a linked list stack stores each element in a node.
- Every node has two parts: the value and a pointer to the next node.
- The top of the stack always points to the last added node.
- We move the top pointer when popping, making it O(1).
This approach eliminates resizing issues but has extra memory overhead for storing pointers.
Python Stack Implementation Using Linked List
class Node:
def __init__(self, data):
self.data = data
self.next = None
class Stack:
def __init__(self):
self.top = None # Points to the top element
def push(self, data):
new_node = Node(data)
new_node.next = self.top
self.top = new_node # Move top to the new node
def pop(self):
if self.is_empty():
return "Stack is empty"
popped = self.top.data
self.top = self.top.next # Move top to the next node
return popped
def peek(self):
return self.top.data if self.top else "Stack is empty"
def is_empty(self):
return self.top is None
def size(self):
count = 0
current = self.top
while current:
count += 1
current = current.next
return count
# Example Usage
stack = Stack()
stack.push(10)
stack.push(20)
print(stack.pop()) # Output: 20
print(stack.peek()) # Output: 10
Pros & Cons of a Linked List Stack
Pros:
- No resizing is needed—it grows dynamically.
- O(1) push/pop with no memory reallocation.
Cons:
- More memory usage—each element stores an extra pointer.
- Slightly slower than array-based stacks due to pointer dereferencing.
For most cases, use deque—it’s faster, cleaner, and scalable. But a linked list stack might be your best bet if you’re working with raw memory or low-level systems.
Applications of Stack Datastructure in Programming
Stacks aren’t just theory—they power some of the most critical features in software. Whenever you hit Undo, navigate back in your browser, or call a recursive function, a stack works behind the scenes.
Let’s break down where and why stacks are used.
Function Call Stack (How Code Runs)
Every function call is pushed onto the call stack, and once it finishes, it’s popped off. This is why recursion works—each recursive call is stored in the stack until it’s time to return.
Example: Factorial Using Recursion (Call Stack in Action)
def factorial(n):
if n == 0:
return 1
return n * factorial(n - 1)
print(factorial(5))
What happens here?
- factorial(5) is pushed onto the stack.
- It calls factorial(4), which is also pushed.
- This continues until factorial(0), which starts returning values back up the stack.
If recursion runs too deep, you hit a StackOverflowError—proving that function calls literally depend on stacks!
Undo/Redo (Text Editors, Photoshop, etc.)
Ever hit Ctrl + Z and magically reversed an action? That’s a stack at work!
- Each action is pushed onto the undo stack.
- When you hit undo, the last action is popped and stored in a redo stack (in case you want to redo it).
Without stacks, text editors wouldn’t know the order of your actions!
Browser History (Back & Forward Navigation)
Ever clicked Back in your browser? That’s another stack-powered feature.
- Back button: Pops the last page from the history stack.
- Forward button: Pushes the last page onto a forward stack.
Depth-First Search (DFS) Algorithm
Depth-First Search (DFS) explores a path as far as possible before backtracking—and it does this using a stack!
def dfs(graph, start):
stack = [start]
visited = set()
while stack:
node = stack.pop()
if node not in visited:
print(node, end=" ")
visited.add(node)
stack.extend(graph[node])
# Example Graph Representation
graph = {
'A': ['B', 'C'],
'B': ['D', 'E'],
'C': ['F'],
'D': [],
'E': ['F'],
'F': []
}
dfs(graph, 'A') # Output: A C F B E D
DFS is used in AI, game development, and solving complex puzzles. Without stacks, it wouldn’t be this efficient!
Balanced Parentheses Check
def is_balanced(s):
stack = [ ]
pairs = {')': '(', '}': '{', ']': '['}
for eachChar in s:
if char in pairs.values():
stack.append(char)
elif char in pairs.keys():
if not stack or stack.pop() != pairs[char]:
return False
return len(stack) == 0
print(is_balanced("({[]})")) # Output: True
print(is_balanced("({[})")) # Output: False
Without stacks, problems like these would require complex recursion or inefficient loops!
Take Your Coding Skills to the Next Level!
Stacks are just the beginning! Want exclusive coding tips, in-depth tutorials, and expert insights delivered straight to your inbox?
Subscribe to my newsletter and get weekly deep dives into data structures, algorithms, and coding best practices.
Join me on YouTube for step-by-step tutorials, live coding sessions, and pro-level problem-solving techniques—all designed to help you become an expert developer.
Don’t just read—take action! Subscribe now and start mastering coding like a pro.