3. Adapting D&C to a NEW problem (the LO1 drill — Applied 2)
Three questions, in order ➔ (1) does the answer decompose additively across the split — within-left + within-right +cross? (2) can the cross term be computed in Θ(n)? (3) does the per-call work shrink with the subproblem, or does the level sum refuse to decay?
Strengthen the recursive contract ➔ ask the recursion to return more than the answer. Counting Inversions is only Θ(nlogn) because each call returns a sorted subarray as well as a count; that extra guarantee makes the cross term linear.
Question (3) is the one that gets skipped ➔ 2D Local Maximum (Peak Finding) halving on the middle column still costs Θ(nlogn) because the deciding scan stays full-length. Cutting both axes makes level i cost cn/2i ⟹ Θ(n).
On a new problem, the correctness argument is the deliverable ➔ “why is it safe to discard the other subproblems?” carries more marks than the pseudocode; state it as an explicit claim about what the kept subproblem is guaranteed to contain.
⚙️ Core Implementation
Split/combine trade-off:Merge Sort = trivial split, heavy combine; Quick Sort = heavy split, trivial combine; Binary Search = single-subproblem “decrease and conquer”.
🔹 The D&C skeleton
generic divide-and-conquer sort
def sort(array) -> None: if len(array) > 1: # base case: len <= 1 already sorted split(array, first_part, second_part) sort(first_part) # conquer each half sort(second_part) combine(first_part, second_part)
💡 Common Mistake:Shrink by factor vs by one ➔ halving gives depth logn, peeling one element gives depth n — the Θ(nlogn) vs Θ(n2) divide and quicksort’s bad-pivot degeneration.
When It Flips: balanced splits give depth logbn; lopsided ones push depth toward n, collapsing Θ(nlogn) to Θ(n2). Space: recursion stack Θ(depth); merge-style combine adds Θ(n) scratch, partition-style is in-place.
📊 Exam Execution Trace
Applied Exercise
Problem: Derive the balanced vs lopsided D&C recurrences.
Final Extracted Output: split balance alone decides Θ(nlogn) vs Θ(n2) — the depth term dominates. (Balanced tree on n=8: log28+1=4 levels, each Θ(8).)
🧠 Active Recall
Why is merge sort Θ(nlogn) but a naïve "process one element then recurse on the rest" is Θ(n2)?
Hint: Factor-shrink vs one-element-shrink.
Answer
Short answer: halving ⟹ log2n levels × Θ(n) = Θ(nlogn); peeling one ⟹ n levels × Θ(n) = Θ(n2).
Why:Balance sets the depth ➔ shrinking by a constant factor vs by one element is the whole difference (∑kk=Θ(n2)) — and quicksort’s bad pivot realises the lopsided case on an otherwise balanced algorithm.
Divide and conquer assumes independent subproblems — what breaks when they overlap, and what replaces it?
Hint: Redundant recomputation.
Answer
Short answer: overlapping subproblems ⟹ redundant work ⟹ exponential (O(2n)).