Chapter 15 Key Takeaways: Dynamic Data Structures
Core Concepts
-
Linked lists grow and shrink at runtime. Unlike arrays, which have a fixed size declared at compile time, linked lists allocate each node individually on the heap. There is no wasted space and no artificial capacity limit.
-
A singly-linked list is a chain of nodes connected by
Nextpointers. The list is accessed through aHeadpointer. Insertion at the head is O(1). Searching and deletion by value are O(n). Adding a tail pointer makes insertion at the tail O(1) as well. -
A doubly-linked list adds a
Prevpointer to each node. This enables bidirectional traversal and simplifies deletion when you already have a pointer to the target node. The cost is one extra pointer per node. -
A stack enforces Last In, First Out (LIFO) access. The only operations are
Push(add to top),Pop(remove from top),Peek(view top without removing), andIsEmpty. All are O(1). Stacks are implemented as linked lists where all operations occur at the head. -
A queue enforces First In, First Out (FIFO) access. The operations are
Enqueue(add to rear),Dequeue(remove from front),Front(view front without removing), andIsEmpty. All are O(1) when using a linked list with bothFrontandRearpointers. -
Circular lists and deques extend the basic structures. A circular list has no
nil— the last node points back to the first. A deque supports insertion and removal at both ends, generalizing both stacks and queues.
Design Decisions
-
Choose arrays for random access and cache performance. Choose linked lists for dynamic sizing and frequent insertions/deletions. Choose stacks for LIFO problems (undo, backtracking, expression evaluation). Choose queues for FIFO problems (scheduling, buffering, BFS).
-
The
Headpointer must be avarparameter in any procedure that might change it (insert at head, delete head, free list). Forgettingvaris the most common linked-list bug. -
Every
Newmust have a matchingDispose. Failing to dispose a node leaks memory. Accessing a node after disposing it creates a dangling pointer. Both are serious bugs that may not crash immediately but will cause problems eventually.
Patterns to Remember
-
Traversal pattern:
Current := Head; while Current <> nil do begin { process } Current := Current^.Next; end; -
Delete pattern: Save node in
Temp, update links to bypass it, thenDispose(Temp). Never dispose before updating links. -
Free-entire-list pattern:
while Head <> nil do begin Temp := Head; Head := Head^.Next; Dispose(Temp); end; -
Undo pattern: Push the inverse of each action onto a stack. To undo, pop and execute the inverse.
PennyWise Progress
-
PennyWise now uses a linked list for expenses — no fixed capacity, no wasted memory, O(1) insertion.
-
PennyWise now supports multi-level undo — each expense addition is recorded on a stack. Undoing pops the stack and removes the matching expense from the list.
Looking Ahead
In Chapter 16, we build on linked structures to create trees — hierarchical data structures where each node can have multiple children. Binary search trees combine the dynamic flexibility of linked lists with the fast lookup of sorted arrays, achieving O(log n) search.