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

  1. 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.

  2. 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.

  3. Constraints are features. Stacks and queues are more restrictive than lists — that's the point. Restrictions prevent accidental misuse and make code self-documenting.

  4. Use collections.deque for queues. Python's list.pop(0) is O(n). The deque.popleft() is O(1). For any FIFO pattern, deque is the right tool.

  5. 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.

  6. 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).

  7. 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.

  8. 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() before pop(), peek(), dequeue(), or front().
  • Off-by-one in queue processing: Use while not q.is_empty() instead of for 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 with deque.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