Expert · Algorithms, data structures and testing · topic 47 of 50 · 30 min
Stacks and queues
Explanation
A data structure is a way of organising data so that certain actions are fast. You already know lists, dicts and sets. Stacks and queues are simpler ideas built on top of them, and they are behind undo buttons, browser history, print queues, ticket counters and task schedulers.
A stack with a list
LIFO stands for Last In, First Out. Adding to a stack is called push; removing the top item is called pop. A Python list does both at its end in O(1): append() pushes and pop() pops. my_list[-1] lets you look at the top without removing it (often called peek).
history = [] # an empty stack
history.append("home") # push
history.append("search")
history.append("product page")
print(history[-1]) # peek: product page
print(history.pop()) # back button: product page
print(history.pop()) # search
print(history) # ['home']Checking balanced brackets with a stack
- Go through the text one character at a time.
- When you see an opening bracket ( [ or {, push it onto the stack.
- When you see a closing bracket, the top of the stack must be its matching opener. If the stack is empty or the top does not match, the text is not balanced.
- At the end, the stack must be empty; leftover openers mean something was never closed.
def is_balanced(text):
pairs = {")": "(", "]": "[", "}": "{"}
stack = []
for ch in text:
if ch in "([{":
stack.append(ch)
elif ch in pairs:
if not stack or stack.pop() != pairs[ch]:
return False
return not stack
print(is_balanced("total = (price * (1 + gst[0]))")) # True
print(is_balanced("print((x)")) # FalseA queue with collections.deque
FIFO stands for First In, First Out. A deque (say "deck", short for double-ended queue) from the collections module adds and removes at both ends in O(1). Use append() to join the back of the queue and popleft() to serve the front.
from collections import deque
counter = deque()
counter.append("Asha") # joins the line
counter.append("Bilal")
counter.append("Chen")
print(counter.popleft()) # Asha is served first
print(len(counter), "still waiting:", list(counter))import time
from collections import deque
n = 20_000
items = list(range(n))
start = time.perf_counter()
while items:
items.pop(0)
list_ms = (time.perf_counter() - start) * 1000
line = deque(range(n))
start = time.perf_counter()
while line:
line.popleft()
deque_ms = (time.perf_counter() - start) * 1000
print("list.pop(0):", round(list_ms, 1), "ms deque.popleft():", round(deque_ms, 1), "ms")Priority queues with heapq
Sometimes the most important item should come out first, not the oldest. That is a priority queue. The heapq module keeps a list arranged as a heap, a tree-like order where the smallest item is always at position 0. heappush and heappop each take O(log n).
import heapq
emergency = []
heapq.heappush(emergency, (3, "sprained ankle"))
heapq.heappush(emergency, (1, "heart attack"))
heapq.heappush(emergency, (2, "high fever"))
while emergency:
priority, case = heapq.heappop(emergency)
print(priority, case) # 1 first, then 2, then 3The idea of a linked list
A linked list stores items in separate little boxes called nodes. Each node holds a value and a pointer (a reference) to the next node, like a treasure hunt where each clue tells you where the next clue is. Adding or removing at the front is fast, because you only change one pointer, but reaching the 500th item means walking through 499 nodes. Python's deque is built from linked blocks for this reason.
| Structure | Order | Python tool | Real use |
|---|---|---|---|
| Stack | LIFO | list: append / pop | Undo, back button, bracket checking |
| Queue | FIFO | collections.deque: append / popleft | Ticket counter, print jobs, order processing |
| Priority queue | Smallest first | heapq: heappush / heappop | Hospital triage, task schedulers, shortest routes |
Watch the lesson
Stacks and queues · 1/7
In this lesson you will learn two simple but powerful data structures: stacks and queues.
Prefer a video by a teacher? Find free videos on “python stacks queues deque heapq tutorial” on YouTube ↗
In real life: Undo in a photo editing app and the order queue of a cloud kitchen
A photo editing app records every change (crop, brighten, add a filter) on a stack; pressing undo pops the last change and reverses it. In the same company, a cloud kitchen receives orders from several delivery apps and cooks them in the order they arrived, using a queue, while VIP or delayed orders jump ahead using a priority queue.
edits = []
for action in ["crop", "brighten", "add filter"]:
edits.append(action)
print("Undo:", edits.pop())
print("Undo:", edits.pop())
print("Remaining edits:", edits)Practice (0/3 done)
Write is_balanced(text) that returns True if every bracket ( [ { in text is closed by the matching ) ] } in the right order, and False otherwise. Other characters are ignored. Use a list as a stack.
Write a class TicketCounter that uses a collections.deque stored in self.queue. join(name) adds a person at the back. serve() removes and returns the person at the front, or None if nobody is waiting. waiting() returns how many people are in the line.
Write next_patients(patients, k). patients is a list of (priority, name) tuples in arrival order; a smaller priority number is more urgent. Return a list of the names of the k most urgent patients, most urgent first. If two have the same priority, the one who arrived first goes first. Use heapq.
Quick quiz
Read more (free)
Pass the quiz (60% or more) or mark this topic as done when you are ready.
Your progress is saved on this device only.