Week 4, Wednesday
January 28, 2026
In the Tuesday video, we saw recurrence forms and their solutions:
| Recurrence | Solution |
|---|---|
| \(T(n) = T(n-1) + 1\) | \(O(n)\) |
| \(T(n) = T(n/2) + 1\) | \(O(\log n)\) |
| \(T(n) = 2T(n/2) + 1\) | \(O(n)\) |
| \(T(n) = 2T(n/2) + n\) | \(O(n \log n)\) |
| \(T(n) = T(n-1) + n\) | \(O(n^2)\) |
Today: How do we prove these solutions?
Expansion method: Unroll the recurrence, find the pattern
Recursion tree method: Visualize as a tree, sum the work
Both give the same asymptotic answer. Choose whichever works for you!
Unroll the recurrence step by step until you see a pattern.
Then apply the pattern to the base case to terminate.
\(T(n) = T(n-1) + c\)
(assume \(n = 2^k\) for simplicity):
\(T(n) = T(n/2) + c\)
(assume \(n = 2^k\) for simplicity)::
\(T(n) = 2T(n/2) + 1\)
Claim: The running time of merge-sort on data of size \(n\) is \(T(n) = O(n\log n)\).
Proof: Prove that there is a constant \(c\) so that \(T(n) \leq cn\log n\) for sufficiently large \(n\).
\(T(n) = 2T(n/2) + n, n > 1\)
By IH, \(T(n)\leq 2\cdot c(n/2)T(\log n/2) + n\)
\(=cn\log n - cn + n\)
\(\leq cn\log n\) when ______
We’ve seen:
Question: Is \(O(n \log n)\) the best we can do?
Or is there some clever algorithm that sorts in \(O(n)\)?
Theorem: Any comparison-based sorting algorithm requires \(\Omega(n \log n)\) comparisons in the worst case.
This means:
Every comparison-based sort makes decisions by comparing pairs of elements:
if arr[i] < arr[j]:
...
else:
...
We can model the algorithm as a binary tree of decisions.
Suppose we want to sort \([v_1, v_2, v_3]\).
Note:
How many different rearrangements are possible for an \(n\) element list?
Each ordering needs its own leaf (otherwise we’d give the same answer for different inputs).
The decision tree must have at least ______ leaves!
Whose answer we need now…
A decision tree of height \(h\) has at most _______ leaves?
Recall: The height of the decision tree is the maximum number of comparisons in the worst case.
If the tree has height \(h\), then:
Therefore: \(2^h \geq n!\)
Taking logarithms: \(h \geq \log_2(n!)\)
Claim: \(\log(n!) = \Omega(n\log n)\)
Observation: Any comparison-based sorting algorithm requires \(\Omega(n \log n)\) comparisons in the worst case.
Justification:
| Algorithm | Worst Case | Optimal? |
|---|---|---|
| Merge Sort | \(O(n \log n)\) | Yes! |
| Heap Sort | \(O(n \log n)\) | Yes! |
| Quick Sort | \(O(n^2)\) | No (but avg is optimal) |
| Insertion Sort | \(O(n^2)\) | No |
| Selection Sort | \(O(n^2)\) | No |
Merge sort achieves the theoretical limit.
The lower bound applies to comparison-based sorts.
What if we don’t compare elements?
Idea: If elements are integers in range \([0, k]\), count occurrences!
Running time: \(O(n + k)\)
If \(k = O(n)\), this is \(O(n)\)—linear time!
Counting sort requires:
Not a general-purpose sort—but incredibly useful when it applies!
| Algorithm | Constraint | Time |
|---|---|---|
| Counting Sort | Integers in \([0, k]\) | \(O(n + k)\) |
| Radix Sort | Fixed-width integers | \(O(d \cdot n)\) |
| Bucket Sort | Uniformly distributed | \(O(n)\) expected |
All exploit extra structure in the input.
Python’s built-in sorted() uses Timsort:
In practice: Just use sorted()!
Lower bounds tell us when to stop searching for better algorithms
The decision tree model appears again in:
Divide-and-conquer analysis (recurrences) applies to:
Monday: Merge Sort + Recurrence Forms
Tuesday Video: Solving Recurrences
Wednesday: Lower Bounds