Brian WilcoxZero → HeroThe Shape of Data: Data Structures
An interactive field guide

Data structures,
from zero to fluent.

A program is data plus the moves you make on it. A data structure is a deliberate way of laying that data out in memory so the moves you need are cheap. This guide takes you from that one idea to the structures every professional reaches for, and every model below runs the real operation and animates it.

15 chapters 14 live models 0 prerequisites read time ~2.5 hrs
Node / cell: a slot that holds data Pointer / edge: a reference to another slot Work: the slot an operation is touching now The answer: where a search lands

Every diagram in this guide uses those four colors and only those four. Learn them once here: teal is the data, indigo is a reference from one place to another, rose is the step currently doing work, amber is the result. Read them once and every model that follows reads at a glance.

Part I
The Cost of Operations
01

Why Structure Matters

Imagine a phone book with a million names in it, and someone asks for "Zimmerman." If the names were in random order, you would have no choice but to read from the first page and keep going until you found it, potentially a million comparisons. Because the names are sorted, you flip to the middle, see you have gone too far or not far enough, and throw away half the book in a single glance. Twenty flips settles a million names.

Nothing changed about the data. The names are the same names. What changed is the arrangement, and the arrangement decided whether the job took twenty steps or a million. That is the entire subject in one sentence.

Data structure
A specific way of organizing data in memory, together with the set of operations it makes cheap. The same values, arranged two different ways, can make the very same question a thousand times faster to answer or a thousand times slower.

A sorted array supports binary search: halve the search space with each comparison. An unsorted pile supports only linear search: check every element until you get lucky. The model below runs both, for real, on the same array. Pick a target and watch how many comparisons each strategy actually makes.

Key idea

Choosing a data structure is choosing which questions are cheap. Every structure ahead is a trade: it makes some operations fast by making others slow, and picking well means knowing which operations you will actually do.

Takeaways
  • The same values in a better arrangement answer the same question far faster.
  • Sorted order unlocks binary search: throw away half the remaining data per step.
  • There is no universally best structure, only the best fit for your mix of operations.
02

Big-O, Honestly

To compare structures we need a way to talk about cost that does not depend on the laptop, the language, or the day. Counting seconds fails all three. So instead we count how the number of basic steps grows as the input grows, and we keep only the part that dominates when the input gets large. That is Big-O notation.

Picture a birthday party. Doubling the guest list does not change how long the oven takes to preheat: flat cost, no matter how many people show up. It does double how long it takes to plate everyone's food: linear cost, one unit of work per guest. But if you insist on introducing every guest to every other guest, the introductions do not double, they quadruple: quadratic cost. How a cost reacts to more input is exactly what Big-O is built to describe.

Big-O
An upper bound on how an algorithm's step count scales with input size n, ignoring constant factors and lower-order terms. O(n) means "grows in proportion to n." O(n²) means "grows in proportion to n squared." We drop constants because for large enough n, the growth rate is what decides the winner.

Why drop the constants? Because growth rate wins every race eventually. An O(n²) method that is cleverly optimized still loses to a sloppy O(n) method once n is big enough, and "big enough" is usually smaller than you would guess. The comparator below plots the five classes you will meet over and over. Drag n and read the actual operation counts: watch the gap between the flat lines and the quadratic one explode.

MODEL 02Growth of the common complexity classes
input size n → operations → O(n²) O(n log n) O(n) O(log n) O(1)
O(1) constant1
O(log n)5
O(n)32
O(n log n)160
O(n²)1024

The counts on the right are computed, not drawn: log₂n rounded up, n, n·log₂n, and . At n = 64, constant time is 1 operation and quadratic time is 4096. That factor of four thousand is the difference between instant and unusable, and it is the reason a professional's first question about any operation is "what is its Big-O?"

Key idea

Big-O measures how cost scales, not how long one run takes. A structure that turns an O(n) operation into O(log n) or O(1) is the whole game, and that is exactly what the rest of this guide is about.

Takeaways
  • Big-O keeps the dominant growth term and drops constants and lower-order terms.
  • Growth rate decides large-input races: O(log n) < O(n) < O(n log n) < O(n²).
  • The prize we chase is turning linear or quadratic operations into logarithmic or constant ones.
03

Arrays vs Linked Lists

There are two fundamentally different ways to store a sequence, and almost every structure ahead is built from one of them. The choice comes down to a single question: are the elements sitting next to each other in memory, or are they scattered and stitched together with pointers?

An array is a parking garage with numbered spaces: knowing the number sends you straight to spot 42, no searching required. A linked list is more like a treasure hunt, where each clue only tells you where the next clue is, so reaching the 42nd clue means finding all 41 before it.

An array is one contiguous block. Element i lives at base + i × size, so the computer jumps straight to any index with one multiply and one add. That is O(1) random access, and it is arrays' superpower. The cost: to insert in the middle, everything after it must shuffle over to make room.

A linked list stores each element in its own node, and each node holds a pointer to the next. The nodes can live anywhere. To reach index i you must start at the head and follow i pointers, one hop at a time: that is O(n) access. The payoff: once you are holding a node, splicing a new one in is O(1) pointer surgery, no shuffling.

Ask the model for a given index under both layouts and count the memory touches. The array lands in one step every time. The list walks.

MODEL 03Random access: array vs linked list
Array (contiguous)
Linked list (pointer hops)
Pick an index and press Access.
Array steps to reach1
List steps to reach6
Array accessO(1)
List accessO(n)
The trade in one line
Arrays: O(1) access, expensive middle insert. Linked lists: O(n) access, cheap insert once you hold the spot. Contiguity buys random access; pointers buy cheap restructuring. You cannot have both for free.
Key idea

Contiguous memory gives instant indexing but rigid structure. Pointer-linked memory gives flexible structure but no shortcuts to the middle. Nearly every structure ahead picks one of these two spines.

Takeaways
  • Array index access is O(1) arithmetic; linked-list access is O(n) pointer walking.
  • Arrays pay to insert in the middle; linked lists pay to find the middle.
  • Contiguity and cache-friendliness make arrays fast in practice more often than the Big-O alone suggests.
Part II
Linear Structures
04

The Dynamic Array

Arrays have one awkward feature: their size is fixed when you make them. But the lists you actually use (a Python list, a Java ArrayList, a C++ vector, a JavaScript array) grow on demand. How? They wrap a fixed array and, when it fills up, allocate a bigger one and copy everything over.

The clever part is how much bigger. If you grew by one slot each time, every single append would copy the whole thing: that is O(n) per append and O(n²) to build a list. Instead, dynamic arrays double the capacity when full. Doubling is the difference between a disaster and one of the most-used structures in computing.

Amortized analysis
Averaging the cost of an operation across a long run, so occasional expensive steps get spread over the many cheap ones. A doubling array's append is O(n) in the worst case (the resize) but O(1) amortized: the total work to append n items is under 2n, so the average per append is a constant.

Here is why doubling works. To reach size n, the resizes copy 1 + 2 + 4 + … + n/2 elements, which sums to less than n. Add the n placements themselves and total work is under 2n: a constant per append. Press the button and watch it: most appends cost one unit, and every so often a tall rose spike marks a resize. The amber line is the running average cost, and it stays flat and low no matter how far you go.

MODEL 04Amortized doubling
Backing array
Cost per append (rose = resize)
Append items and watch the capacity double.
Size0
Capacity1
Last append cost0
Total elements copied0
Amortized cost / append0.00
Key idea

Doubling turns a growable array into an amortized O(1) append. A rare expensive resize is paid for by all the cheap appends around it, so the average stays constant. This is why the default "list" in every language is a dynamic array.

Takeaways
  • Dynamic arrays double capacity on overflow, copying the old contents once.
  • Appends are O(n) worst case at a resize but O(1) amortized (total work under 2n).
  • Growing by a constant amount instead of doubling would make building a list O(n²).
05

Linked Lists

Think of a linked list as a train: each car is coupled only to the next one. Adding a new car at the front is one coupling, and nothing else in the train has to move. But reaching the tenth car means walking the couplings from the front, one at a time.

A linked list is the pointer-spine sequence from Chapter 3, made concrete. Each node holds a value and at least one pointer. In a singly linked list every node points only to the next; in a doubly linked list each node also points back to the previous, so you can walk in both directions and delete a node you are standing on without hunting for its predecessor.

The reason to reach for a linked list is cheap splicing. Inserting at the head is pure pointer work: make the new node point at the old head, then move the head pointer. No element ever moves in memory. The model animates the surgery: watch the pointers rewire as you insert and delete.

MODEL 05Linked list: pointer surgery
Insert and delete to see pointers rewire.
Length0
Forward pointers0
Back pointers0
Insert / delete at headO(1)
Singly vs doubly
Singly linked lists use one pointer per node: less memory, forward traversal only. Doubly linked lists add a back-pointer: twice the pointer overhead, but O(1) deletion of a known node and backward traversal. Doubly linked lists back many queues, LRU caches, and editor undo stacks.
Key idea

Linked lists trade random access for restructuring. When you constantly splice and remove at ends or known positions and rarely index, the linked list wins. When you index constantly, it loses badly.

Takeaways
  • Each node stores a value plus a pointer to the next (and, if doubly linked, the previous).
  • Head insert and delete are O(1): no elements move, only pointers change.
  • Doubly linked lists cost an extra pointer per node but enable O(1) deletion of a held node.
06

Stacks

A stack is the most disciplined structure there is: you may only add to the top, and only remove from the top. Last in, first out (LIFO), like a stack of plates. That single restriction is exactly what you want for anything that nests: function calls, undo history, matching brackets, the back button, depth-first search.

Stack (LIFO)
A collection with two O(1) operations: push adds an item to the top, pop removes and returns the top. The most recently pushed item is always the first one popped. Backed by either a dynamic array or a linked list.

The call stack your program runs on is literally this: each function call pushes a frame, each return pops one. Push and pop touch only the top, so both are O(1). Drive it below and watch LIFO order fall out: the last thing you push is the first thing that comes back.

MODEL 06Stack: last in, first out
bottom of stack
Push values on, pop them off. Watch the order reverse.
Size0
Top elementempty
Last poppednone
push / popO(1)
Key idea

A stack is an array with the middle taken away on purpose. By allowing access only at the top it guarantees O(1) push and pop and models every process that nests.

Takeaways
  • Stacks are LIFO: the last item pushed is the first popped.
  • Both operations touch only the top, so both are O(1).
  • The program call stack, undo, bracket matching, and depth-first search are all stacks.
07

Queues & Ring Buffers

Flip the stack's rule and you get a queue: add at the back, remove from the front. First in, first out (FIFO), like a line at a counter. Queues model anything served in arrival order: print jobs, task schedulers, request pipelines, breadth-first search.

The naive way to build one on an array is to dequeue by removing element 0 and shifting everything left, which is O(n) every time. The professional way is a ring buffer: a fixed array with two indices, head and tail, that wrap around modulo the capacity. Nothing shifts. Enqueue writes at tail and advances it; dequeue reads at head and advances it; both wrap to 0 when they run off the end. Both operations become O(1).

MODEL 07Ring buffer: FIFO that wraps
Fixed array, indices wrap mod 8
Enqueue past the end and watch tail wrap to index 0.
Size0
Head index0
Tail index0
Front valueempty
enqueue / dequeueO(1)
Ring buffer
A queue on a fixed array where head and tail advance with modular arithmetic (index = (index + 1) mod capacity). It gives O(1) enqueue and dequeue with zero shifting and a fixed memory footprint, which is why it underlies audio buffers, network stacks, and streaming pipelines.
Key idea

A queue is a stack's mirror image: FIFO instead of LIFO. Implemented as a ring buffer with wrapping indices, both ends cost O(1) and no element ever moves.

Takeaways
  • Queues are FIFO: served in arrival order.
  • A ring buffer wraps head and tail modulo capacity, so enqueue and dequeue are O(1).
  • Breadth-first search, schedulers, and streaming buffers all ride on queues.
Part III
Hashing
08

Hash Functions

Everything so far has been about arranging data so you can find things. Hashing is the idea that gets you the holy grail: finding a value in constant time on average, no matter how many values there are. The trick is to compute where something should live directly from the value itself.

A hash function takes a key of any size (a string, say) and grinds it into a fixed-size integer. Good hash functions have two properties: they are deterministic (the same key always produces the same number) and they spread keys evenly (small changes in the key scatter the output). To turn that big number into an array index, take it modulo the table size.

Think of a valet who has to find your car in a giant lot. A valet who just remembers every car as it arrives has to scan the whole lot looking for yours. A smarter valet computes your spot from your license plate the moment you hand over the keys, then walks straight to it. That calculation, an address computed from the content itself, is what a hash function does.

index = hash(key) mod tableSize
hash(s) = ( … (s₀ × 31 + s₁) × 31 + s₂ … ) as a 32-bit integer

The model runs exactly that polynomial hash (the one Java's String.hashCode uses). Type a key and watch the integer accumulate character by character, then collapse to a bucket index. Type the same key twice and you land in the same bucket every time: that determinism is what makes lookup possible.

MODEL 08Hashing a key to a bucket
hash = 0
32-bit hash: 0
index = hash mod 8 = 0
Buckets
32-bit hash0
Table size8
Bucket index0
Deterministic?always
Key idea

A hash function computes an item's address from its contents. That single move replaces searching with arithmetic: instead of looking for a key, you calculate where it must be.

Takeaways
  • A hash function maps a key to a fixed-size integer, deterministically.
  • Modulo the table size turns that integer into a bucket index.
  • Good hashes spread similar keys far apart so buckets fill evenly.
09

Hash Tables

Picture a wall of PO boxes. Your key decides which box your mail goes into. Two different keys can land on the same box, a collision, and there are two ways to handle it: chaining stuffs both people's mail into that one box, open addressing walks down the row and hands the second person the next open box instead.

A hash table is an array of buckets plus a hash function that says which bucket a key belongs in. Insert, look up, and delete all compute the bucket in O(1) and go straight there. This is the structure behind Python's dict, JavaScript's Map and objects, database indexes, and caches everywhere.

The complication is collisions: two different keys can hash to the same bucket. There are two classic fixes. Chaining stores a little linked list in each bucket and appends colliders. Open addressing (linear probing) keeps one item per slot and, on a collision, walks to the next empty slot. Toggle between them below and watch collisions resolve two different ways.

Load factor
The ratio α = items / buckets. It controls how crowded the table is, which controls how often collisions happen. When α crosses a threshold (commonly 0.75), the table resizes: it allocates a bigger array and rehashes every item into it. Keeping α bounded is what keeps operations O(1) on average.

Insert keys one at a time. The collision counter ticks when a key lands on an occupied bucket, and when the load factor crosses 0.75 the whole table doubles and rehashes. That resize is why average-case lookup stays O(1): a bounded load factor means the average chain is a constant length, no matter how many keys you store.

MODEL 09Hash table: collisions, load factor, resize
Insert keys and watch buckets fill.
Keys stored0
Buckets8
Load factor0.00
Collisions so far0
Resizes0
Key idea

A hash table trades a little memory and a good hash function for average O(1) insert, lookup, and delete. Bounding the load factor by resizing keeps collisions rare, which is what keeps the average constant. Worst case is still O(n) if everything collides, which is why hash quality matters.

Takeaways
  • A hash table is buckets plus a hash function: operations compute the bucket directly.
  • Collisions are handled by chaining (a list per bucket) or open addressing (probe to the next slot).
  • Resizing when the load factor crosses a threshold keeps average operations O(1).
Part IV
Trees
10

Binary Search Trees

Hash tables are unbeatable for exact lookup, but they scatter keys into buckets in no useful order. The moment you need sorted order, ranges ("all names between H and P"), or the next-largest key, you want a tree. The workhorse is the binary search tree (BST).

Think of the number-guessing game: I am thinking of a number between 1 and 100, you guess and I tell you higher or lower. You would not guess 3, then 4, then 5. You would guess 50, then 25 or 75, cutting the range in half every time. A binary search tree bakes that exact strategy into how it stores data.

Binary search tree
A tree where every node has up to two children and one ordering rule holds everywhere: every key in a node's left subtree is smaller, every key in its right subtree is larger. That invariant makes search a walk downward: at each node, go left if your target is smaller, right if larger.

The ordering rule is the whole trick. To search, start at the root and compare: smaller goes left, larger goes right. Each comparison discards an entire subtree, exactly like binary search on the phone book, except the structure is explicit and stays cheap to update. Insert follows the same path and hangs the new node where the walk falls off. Reading the tree left-to-right (an in-order traversal) always yields sorted keys.

Drive the real tree below. Search animates the path down and lands on the answer in amber; insert and delete restructure it live.

MODEL 10Binary search tree: insert, search, delete
Insert a key, or search for one and watch the path.
Nodes0
Height0
Last search
In-order (sorted)
Key idea

A BST keeps keys in sorted order while supporting search, insert, and delete in time proportional to the tree's height. When the tree is bushy, height is about log n and everything is fast. That "when" is the catch, and it is the next chapter.

Takeaways
  • A BST invariant: left subtree smaller, right subtree larger, everywhere.
  • Search, insert, and delete each walk one root-to-leaf path, costing O(height).
  • In-order traversal reads the keys out in sorted order for free.
11

Why Balance Matters

A BST's operations cost O(height), and the previous chapter quietly assumed height is about log n. That is only true if the tree stays balanced. Feed a plain BST keys that are already sorted and something ugly happens: every new key is larger than the last, so it hangs off the right side, and the tree degenerates into a linked list of height n. Search is back to O(n). All the cleverness is gone.

The model proves it. Insert 15 keys in sorted order and the height is 14, a straight diagonal. Insert the same 15 keys shuffled and the height collapses toward log₂15 ≈ 4, a bushy tree. Same keys, same structure, wildly different shape and cost, decided entirely by insertion order.

MODEL 11Degenerate vs balanced
Insert the same keys sorted, then shuffled.
Keys0
Tree height0
Balanced ideal0
Search costO(?)

This is why production code rarely uses a plain BST. Self-balancing trees (AVL trees, red-black trees) do a little extra pointer rotation on each insert to guarantee the height stays O(log n) no matter what order keys arrive in. B-trees extend the same idea to wide, shallow trees tuned for disk and are what SQL databases and filesystems index with. The balancing details differ, but the mission is identical: never let the tree become a list.

Key idea

A BST is only as fast as it is balanced. Adversarial (sorted) input degrades it to O(n). Self-balancing trees pay a small rotation cost per insert to guarantee O(log n) height forever, which is why they, not plain BSTs, sit under real databases.

Takeaways
  • BST cost is O(height), and height depends entirely on insertion order.
  • Sorted input degenerates a plain BST into a linked list of height n.
  • AVL, red-black, and B-trees rebalance on insert to keep height O(log n) guaranteed.
12

Heaps & Priority Queues

Sometimes you do not need everything sorted, you just need the most urgent thing next, over and over: the shortest task, the closest point, the highest-priority packet. That is a priority queue, and the structure that implements it beautifully is the binary heap.

Binary heap
A complete binary tree with the heap property: every parent is smaller than (or equal to) its children (a min-heap). The minimum is therefore always at the root. Because the tree is complete, it packs perfectly into an array: node i's children are at 2i+1 and 2i+2. No pointers needed.

Two operations keep the heap property. Insert drops the new value at the end of the array and sifts it up, swapping with its parent while it is smaller. Extract-min removes the root, moves the last element to the top, and sifts it down, swapping with its smaller child until order is restored. Each is O(log n) because the tree is only log n tall. The model shows both the tree and its backing array, and extract-min repeatedly gives you a perfectly sorted sequence.

MODEL 12Binary min-heap: sift up, sift down
Backing array
Insert values, then extract the minimum repeatedly.
Size0
Minimum (root)empty
Last extractednone
insert / extractO(log n)
Key idea

A heap keeps only a partial order, just enough to know the extreme element instantly. Insert and extract-min are O(log n), and it lives in a flat array with no pointers, which makes it the go-to priority queue and the engine inside heapsort and Dijkstra's algorithm.

Takeaways
  • A min-heap keeps the smallest element at the root via the parent-≤-children property.
  • Insert sifts up, extract-min sifts down, each O(log n).
  • A complete tree packs into an array (children of i at 2i+1, 2i+2), no pointers required.
Part V
Graphs
13

Graphs

Trees are a special case of the most general structure of all: the graph, a set of vertices connected by edges. Roads between cities, friendships in a social network, links between web pages, dependencies between build steps: all graphs. A tree is just a graph with no cycles and a single path between any two nodes.

There are two ways to store the edges, and the choice drives everything. An adjacency matrix is a V × V grid where cell (i,j) is 1 if an edge exists. Checking "is there an edge from i to j?" is instant, but it always costs memory even if the graph is nearly empty. An adjacency list stores, for each vertex, a list of its neighbors: memory is V + 2E (each undirected edge listed twice), which is tiny for the sparse graphs that dominate the real world.

MODEL 13Adjacency list vs matrix, and BFS
Run breadth-first search from vertex 0.
Vertices (V)6
Edges (E)7
Matrix memory36
List memory20
BFS order

The model also runs breadth-first search (BFS), which explores a graph level by level using the queue from Chapter 7. From the start vertex it visits all neighbors, then all their unvisited neighbors, and so on, giving the shortest path in edges. BFS is the bridge from data structures to graph algorithms: once you can store a graph, the queue and the stack unlock traversal, shortest paths, connectivity, and much more.

Key idea

Graphs generalize every linked structure. Store dense graphs as a matrix for O(1) edge tests, sparse graphs (almost all real ones) as adjacency lists for O(V+E) memory. Traversal reuses the queue and stack you already know.

Takeaways
  • A graph is vertices plus edges; trees are the acyclic, singly-connected special case.
  • Adjacency matrix: O(1) edge check, O(V²) memory. Adjacency list: O(V+E) memory, cheap for sparse graphs.
  • BFS uses a queue to explore level by level and find shortest paths in edges.
Part VI
Strings
14

Tries

Some data has structure a hash table throws away. Words share prefixes: "car," "card," and "care" all begin with "car." A trie (prefix tree) exploits that. Instead of storing whole keys, it stores one character per edge, so every shared prefix is stored exactly once and each word is a path from the root.

Trie
A tree where each edge is labeled with one character and each path from the root spells a prefix. Nodes flagged as word-ends mark complete keys. Lookup and insert take O(L) time for a key of length L, independent of how many keys are stored, and every shared prefix is stored once.

The payoff is prefix queries. "Give me every word starting with 'ca'" is just: walk to the "ca" node, then collect everything below it. That is how autocomplete, spell-checkers, IP routing tables, and search suggestions work. Insert words below and search by full word or by prefix; the walk animates character by character.

MODEL 14Trie: a tree of shared prefixes
Insert words, then search a word or a prefix.
Words stored0
Nodes1
Result
lookupO(L)
Key idea

A trie makes the shape of strings into the shape of the structure. Lookup cost depends only on key length, not on the number of keys, and prefix queries fall out for free, which is why autocomplete is a trie.

Takeaways
  • A trie stores one character per edge; shared prefixes are stored once.
  • Insert and lookup are O(L) in the key length, independent of the number of keys.
  • Prefix queries (autocomplete, routing) are a walk to a node plus a collect below it.
Part VII
Capstone
15

Choosing a Structure

You now have the whole toolbox. Fluency is not memorizing every structure, it is matching the structure to the operations you will actually perform. The question is never "what is the best data structure?" It is "what do I do to this data most often, and which layout makes that cheap?"

Here is the cost map for the operations that decide most choices. Colors run from cheap through logarithmic and linear to quadratic-ish, so you can read the trade-offs at a glance.

StructureAccessSearchInsertDeleteOrdered?
Dynamic arrayO(1)O(n)O(1)*O(n)no
Linked listO(n)O(n)O(1)O(1)no
Stack / QueueO(n)O(n)O(1)O(1)no
Hash tableO(1)†O(1)†O(1)†no
Balanced BSTO(log n)O(log n)O(log n)O(log n)yes
Binary heapO(n)O(log n)O(log n)‡min/max
TrieO(L)O(L)O(L)O(L)prefix

* amortized. † average case; worst case O(n). ‡ extract-min only. L is key length.

The decision, distilled

  • Index by position, iterate a lot? Dynamic array.
  • Constant splicing at the ends or a known node? Linked list.
  • Strict nesting (LIFO) or arrival order (FIFO)? Stack or queue.
  • Exact-match lookup by key, order irrelevant? Hash table.
  • Need sorted order, ranges, or nearest key? Balanced BST.
  • Always want the most extreme element next? Heap.
  • Prefix queries over strings? Trie.
  • Arbitrary relationships between things? Graph.
Key idea

Every structure in this guide is a deliberate trade: it buys speed on the operations you need by giving up speed on the ones you do not. Fluency is knowing your operation mix and reading this table without looking.

Where to Go Next

Data structures are the nouns of computing. The natural next step is the verbs: algorithms that act on them. You are already primed for it, because half of algorithm design is picking the right structure.

  • Sorting: mergesort and quicksort (O(n log n)), and how heaps give heapsort. The array is the stage.
  • Graph algorithms: Dijkstra and A* (a heap plus a graph), topological sort, minimum spanning trees, union-find.
  • Dynamic programming: turning exponential recursion into polynomial tables, memoized with arrays and hash maps.
  • Balanced trees in depth: red-black trees, B-trees, and the log-structured merge trees inside modern databases.
  • Probabilistic structures: Bloom filters, skip lists, and HyperLogLog, where a little randomness buys huge memory savings.

The canonical text is Cormen, Leiserson, Rivest, and Stein, Introduction to Algorithms. For a gentler, code-first route, Sedgewick and Wayne's Algorithms pairs well with everything here. But the fastest way to make these permanent is to implement each structure once, from scratch, and watch it run. You just did that fourteen times.

One last idea

Master a dozen structures and their costs and you can reason about the performance of almost any program before you write a line of it. That is the quiet superpower this subject hands you.

More from Zero → Hero

Browse all 15 guides →