Algorithms I: Analysis, Divide & Conquer, and Data Structures
Every algorithm faces two questions: is it correct, and how fast. Correctness is argued by induction (a loop/recursion invariant), by contradiction, or by an exchange argument (13b); speed is read off a recurrence, solved by the master theorem, a recursion tree, or substitution. On those rails sit the first design paradigm, divide & conquer — split, recurse, combine — and its two refinements: randomization, which buys a good expected runtime when no deterministic choice is safe, and the realization that comparison-based sorting cannot beat (a counting lower bound), so linear-time sorts must avoid comparing. The closing third is the reusable speed primitives — data structures (heaps, balanced BSTs, hash tables) — whose entire job is to make a repeated operation cheap, almost always by guaranteeing height or expected lookup. A single meta-move recurs: write the work as a recurrence or a sum of indicator variables, then bound it. Prerequisites cashed in: logs and geometric/harmonic sums (track 1); induction and proof structure (track 2); indicator RVs, linearity of expectation, Bernoulli/geometric expectations (track 1); the quicksort/BST kinship to pivoting (this guide).
1. Asymptotics and proving runtimes
- The three bounds. (upper, ); (lower, ); (both — asymptotically tight). Little- is the strict version (), a looser, absolute bound than . Constants and lower-order terms wash out; what survives is growth rate.
- Proving an or bound needs only one witness pair making the inequality hold for all — you don’t need it for all . Proving = prove and separately.
- Disproving a claimed bound: by contradiction, manipulate the inequality until is pinned below/above a constant; then any past that constant breaks it.
- A bound to know cold: . Upper: so . Lower: the top factors each exceed , so and . (This is the sorting lower bound of §5.)
- Correctness tools kept on hand: induction (state the invariant — what holds after iteration — then base case + inductive step), contradiction (assume the negation, derive impossibility), and the exchange argument for greedy (13b).
Core competency set
- State the definitions and what each asserts; prove a bound by exhibiting .
- Prove both directions.
2. Recurrences — solving the cost of recursion
- The master theorem. For with constants ( = number of recursive calls, = input-shrink factor, = exponent of the non-recursive “combine” work):
- Why — the recursion tree (derive it). At level there are subproblems, each of size , so the work at level is
Summing over levels is a geometric series in ratio :
- (): the series is dominated by its first term — the root does the most work → ;
- (): every level does equal work , and there are levels → ;
- (): dominated by the last term — the leaves dominate, and there are of them → .
So the whole theorem is one question: do subproblems proliferate () faster or slower than the per-call work shrinks ()?
- Numeric anchor (one knob, three regimes): fix with a linear combine (, so ) and vary only . : , → (root-dominated). : mergesort, → (every level equal work). : → (leaf-dominated). The phase change sits exactly at .
- General (CLRS) form for : compare to ; if is polynomially smaller → ; equal (to within a log) → ; polynomially larger and the regularity condition for → . (Case 3’s regularity condition is the one to check carefully.)
- Reshaping a recurrence to fit. For : substitute (so ), giving ; define to get , solve by master ( → ), then back-substitute → .
- Substitution method (when the master theorem doesn’t apply — e.g. unequal children ): guess the answer, prove it by induction. Guess ; assume it for all ; substitute into the recurrence and choose (and ) so the inductive step closes. The signature trick for DSelect (§3): the two recursive fractions sum to less than 1, so the guess survives — linear time.
- Recursion trees are also the most reliable hands-on tool. Picture (draw it): root = the size- problem; level has nodes of size , each doing work, so the per-level totals form the geometric series in — a top-heavy triangle if (root dominates), uniform bars if , bottom-heavy if (leaves dominate). Draw the levels, size and count the work, sum.
Core competency set
- State the master theorem and derive its three cases from the recursion-tree geometric series ().
- Solve a recurrence by substitution (guess + induct) and reshape one by variable substitution.
3. Divide and conquer
- The recipe: divide the input into smaller subproblems, conquer them recursively, combine. Always start with the brute force — it sets the runtime to beat — then ask: is any work duplicated or superfluous that the recursion can cut (Karatsuba’s 4→3 multiplications), or that an invariant lets you skip?
- Where the cleverness hides: in the combine step (mergesort’s merge), before the recursion (quicksort’s pivot/partition), or in the recursion structure itself. Heuristic: thinking about element values → consider sorting; thinking about indices/order → compare values at chosen indices; need a global answer (mergesort) vs. one element (search, select).
- Worked recurrences:
- binary search (one recursive call, so ).
- mergesort ().
- Karatsuba multiplies two -digit numbers with 3 (not 4) subproducts: .
- max-subarray: the max subarray lies in the left half, the right half, or crosses the midpoint; check the crossing one in by extending outward from the middle, recurse on the two halves: .
- Selection (the -th order statistic).
- RSelect: pick a random pivot, partition, recurse on the side containing rank (no combine). A good (near-median) pivot gives ; worst case ; expected because random pivots are usually good.
- DSelect (deterministic linear): pick the pivot as the median of medians — split into groups of 5, take each group’s median, recurse to find their median. This guarantees the pivot beats/loses to at least 30% on each side (a split in the middle 40%), so
Since $\tfrac15 + \tfrac{7}{10} = \tfrac{9}{10} < 1$, substitution gives $T(n) = O(n)$ — the sub-call fractions summing below 1 is exactly what makes it linear.
- Design heuristics: a for-free primitive — if you’re already spending , an extra step costing (e.g. a sort) is free and may simplify everything; and converting a non-recursive method to recursive form can expose duplicate work to remove.
Core competency set
- State the D&C recipe and where the cleverness can hide; give the binary-search/mergesort/Karatsuba/max-subarray recurrences and answers.
- Explain RSelect (expected ) and the DSelect median-of-medians split with the argument.
4. Randomized algorithms
- The decomposition method (the core analysis skill): to bound an expected count, (1) name the count RV ; (2) write it as a sum of indicator (0/1) variables ; (3) apply linearity of expectation ; (4) compute each and sum. Linearity needs no independence — that’s what makes it powerful.
- Quicksort is expected (the canonical decomposition). Sorting elements , let ever compared. They are compared iff one of is the first pivot chosen among the elements — if any element strictly between them is picked first, it splits them into different subarrays and they never meet. Of those equally-likely first picks, 2 cause a comparison, so
Then by linearity,
Each inner sum is at most (the harmonic number ), and there are of them:
(Picking the first element of a sorted array is the worst case; the median pivot would match mergesort — random pivots get you there in expectation.)
- Las Vegas vs Monte Carlo. Las Vegas = always correct, runtime is random (quicksort, RSelect — randomness only affects speed). Monte Carlo = always fast, sometimes wrong (Karger, 13b — randomness affects correctness, repeat to drive failure probability down).
- Sums to keep handy: geometric , and for bounded by ; harmonic ; .
Core competency set
- State the 4-step decomposition method and run it for quicksort: .
- Contrast Las Vegas vs Monte Carlo with an example of each.
5. Sorting lower bound and linear-time sorts
- The comparison model as a decision tree. Any comparison-based sort is a binary tree: internal nodes are comparisons (“is ?”), the two branches are the boolean answers, and each leaf is one of the possible output orderings. Running the algorithm on a particular input traces one root-to-leaf path; the worst-case number of comparisons is the tree’s height.
- The lower bound (count the leaves). A correct sort must be able to output any of the permutations, so the tree has leaves. A binary tree with leaves has height , so
Hence every comparison sort is — deterministically in the worst case, and in expectation for randomized ones. Mergesort/heapsort meet it.
- Beating it requires not comparing — use the values themselves as addresses:
- bucket sort: one FIFO bucket per possible value; drop each element into its bucket (), then concatenate buckets in order (). total, but you must know the value range and it must be small.
- radix sort: bucket-sort by one digit at a time, least-significant first, using FIFO buckets to preserve earlier order (stability). For integers up to in base : digits, per pass, total . Choosing gives , which is when for constant — and degrades when .
Core competency set
- Set up the decision-tree model and prove the comparison lower bound via leaves.
- Give bucket and radix sort and the condition () under which radix is linear.
6. Data structures — the speed primitives
-
The motivating tradeoff. A sorted array searches in but inserts/deletes in (shifting memory); a linked list inserts in but searches in . Dynamic structures aim to get both — and the recurring lever is bounding the height of a tree to .
-
Heap — a complete binary tree (every level full except the last, filled left-to-right) with the heap order (every node’s key its descendants’). Supports fast insert and extract-min:
- insert: place at the next open slot (keeps completeness), then bubble up — swap with the parent while smaller — at most ;
- extract-min: the root is the min; remove it, move the last element to the root, then bubble down — swap with the smaller child while larger — .
Array-backed (node ‘s children at ); not a unique ordering of the elements. Numeric anchor: min-heap , insert → place at slot 4 (under the ), bubble up: swap, then swap → (two swaps ). Extract-min: pop , move last () to root, bubble down past the smaller child → back to . Picture (draw it): a heap is a left-filled triangle, small-on-top; a balanced BST is the same triangle but sorted left-to-right (in-order traversal = sorted); a degenerate BST is a single diagonal chain of height — the picture of why balancing (red-black) matters.
-
Binary search tree (BST) — invariant: every left descendant node every right descendant. In-order traversal (left, node, right) prints the keys sorted in . Search/insert/delete/min/max all walk one root-to-leaf path → . Height is at best but for a degenerate chain — so balance is everything, and runtime is honestly stated as .
-
Red-black tree — a self-balancing BST. Invariants (4): (1) valid BST; (2) root is black; (3) a red node’s children are black; (4) every root-to-NIL path has the same number of black nodes (the “black-height”). These force the tree to stay within a factor 2 of balanced — red nodes mark where a path is getting long.
- Height bound: . Proof (induction on subtree size): claim a subtree rooted at with black-height has internal nodes. Base: a single node. Step: with children ,
using $b(y), b(z) \ge b(x)-1$. So $n \ge 2^{b}-1$, giving black-height $b = O(\log n)$; since red nodes can at most double a path, the full height is $O(\log n)$. All operations are then $O(\log n)$.
- Hash table — expected insert/delete/search when you don’t need ordering. Use chaining (a linked list per bucket); the goal is to spread keys evenly over buckets so chains stay length . The catch: for any fixed hash function an adversary can pick keys that all collide, so you must randomize the choice of function — but storing an arbitrary random function costs more than the table. Resolve with a universal family: a small family with
Then the expected bucket size for key is bounded:
so search is expected. Construction: pick a prime and set for — a universal family of size storable in bits. (To check universality, find a key pair forced into the same bucket more than a fraction of the time — e.g. a plain “mod” family fails.)
Core competency set
- Give the sorted-array vs linked-list tradeoff and state heap insert/extract-min as .
- State the BST invariant and why runtime is ; list the 4 red-black invariants and prove the height via .
- Explain why a fixed hash function is unsafe, define a universal family, derive , and give the construction.
7. Memorize cold
- need one witness; is strict. .
- Master theorem : ; ; . (Ratio : root vs leaves.)
- mergesort ; binary search ; Karatsuba ; max-subarray .
- RSelect expected ; DSelect median-of-medians (since ).
- Decomposition: . Quicksort: expected.
- Las Vegas (always right, random time) vs Monte Carlo (always fast, sometimes wrong).
- Comparison sort lower bound: leaves height . Radix when .
- Heap / BST ops ; balanced . Red-black height ; proof .
- Universal hashing: ; , prime.
Named moves (cross-track glossary): brute-force-first (sets the runtime to beat); recursion-tree (sum the geometric series in — root vs leaves); guess-and-induct (substitution method); reshape-the-recurrence (variable substitution ); decomposition-into-indicators (, no independence needed — track 1); the-good-pivot (random or median-of-medians); fractions-sum-below-1 (DSelect linearity); count-the-leaves (decision-tree lower bound); don’t-compare (bucket/radix beat ); -then-balance (BST → red-black); randomize-the-hash (universal family defeats the adversary); for-free-primitive (a step inside your existing budget is free).