Chapter 20 Key Takeaways
One-Sentence Summary
Specialized data structures like stacks (LIFO) and queues (FIFO) constrain operations to prevent bugs and communicate intent — and the idea of separating what a data structure does from how it's built (abstract data types) is one of the most powerful concepts in computer science.
Core Concepts
-
A stack is Last-In, First-Out (LIFO). The last item pushed is the first item popped. Use stacks for undo systems, bracket matching, backtracking, and anywhere the most recent item matters most.
-
A queue is First-In, First-Out (FIFO). The first item enqueued is the first item dequeued. Use queues for task scheduling, processing pipelines, print spoolers, and breadth-first search.
-
Constraints are features. Stacks and queues are more restrictive than lists — that's the point. Restrictions prevent accidental misuse and make code self-documenting.
-
Use
collections.dequefor queues. Python'slist.pop(0)is O(n). Thedeque.popleft()is O(1). For any FIFO pattern,dequeis the right tool. -
An abstract data type (ADT) separates interface from implementation. The stack ADT says "support push, pop, peek." It doesn't say "use a list internally." This separation lets you swap implementations without changing client code.
-
Linked lists trade random access for fast insertion. Each node stores data and a reference to the next node. You can't jump to the 50th element (O(n)), but you can insert/delete at a known position in O(1).
-
Trees, graphs, and hash tables are CS2 territory. Trees model hierarchy, graphs model relationships, hash tables provide O(1) key-value lookup. Understanding what they're for prepares you for deeper study.
-
Choose the right data structure for the problem's shape. Match LIFO problems to stacks, FIFO problems to queues, key-value problems to dicts, uniqueness problems to sets, and hierarchy problems to trees.
Key Operations at a Glance
Stack Operations
| Operation | Description | Time Complexity |
|---|---|---|
push(item) |
Add to the top | O(1) |
pop() |
Remove and return top item | O(1) |
peek() |
View top item without removing | O(1) |
is_empty() |
Check if stack is empty | O(1) |
size() |
Number of items | O(1) |
Queue Operations
| Operation | Description | Time Complexity |
|---|---|---|
enqueue(item) |
Add to the back | O(1) |
dequeue() |
Remove and return front item | O(1) with deque |
front() |
View front item without removing | O(1) |
is_empty() |
Check if queue is empty | O(1) |
size() |
Number of items | O(1) |
Data Structure Comparison
| Structure | Access Pattern | Best For | Avoid When |
|---|---|---|---|
list |
Random access by index | Ordered collections, iteration | You need fast removal from the front |
dict |
Key-value lookup | Associative data, counting, caching | You need ordered iteration (pre-3.7) |
set |
Membership testing | Deduplication, fast in checks |
You need to preserve insertion order |
tuple |
Immutable sequence | Fixed records, dict keys | You need to modify the contents |
| Stack | LIFO | Undo, backtracking, parsing | You need FIFO or random access |
| Queue | FIFO | Scheduling, pipelines, BFS | You need LIFO or random access |
Common Patterns
# Stack pattern: process in reverse order
while not stack.is_empty():
item = stack.pop()
process(item)
# Queue pattern: process in arrival order
while not queue.is_empty():
item = queue.dequeue()
process(item)
# Always guard before popping/dequeuing
if not stack.is_empty():
top = stack.peek() # safe
# Undo/redo: two stacks
undo_stack.push(current_state)
# to undo: redo_stack.push(current_state); current_state = undo_stack.pop()
# to redo: undo_stack.push(current_state); current_state = redo_stack.pop()
Debugging Reminders
- Empty stack/queue errors: Always check
is_empty()beforepop(),peek(),dequeue(), orfront(). - Off-by-one in queue processing: Use
while not q.is_empty()instead offor i in range(n)to avoid processing one too many or one too few. - Redo stack not clearing: When implementing undo/redo, any new action must clear the redo stack. Forgetting this creates impossible "time travel" scenarios.
- Using
list.pop(0)for a queue: Replace withdeque.popleft()for O(1) performance.
Checklist: Before Moving to Chapter 21
- [ ] Can implement a Stack class with push, pop, peek, is_empty, size
- [ ] Can implement a Queue class using
collections.deque - [ ] Can use a stack for bracket matching
- [ ] Can explain the undo/redo two-stack pattern
- [ ] Can explain what an abstract data type (ADT) is and why it matters
- [ ] Can describe linked lists, trees, graphs, and hash tables at a conceptual level
- [ ] Can choose the right data structure for a given problem
- [ ] Built TaskFlow v1.9 with undo/redo and task queue