Divide and conquer
The divide and conquer paradigm: split a problem into independent smaller copies, solve them recursively, combine the answers. The recurrence T(n) = aT(n/b) + f(n), what the three Master theorem cases mean for where the work is done, the maximum-and-minimum example (3n/2 - 2 comparisons), correctness by induction, stack depth, and why overlapping subproblems need dynamic programming instead.
Why it exists
Many problems have the same shape at every scale: sorting a million numbers is two problems of sorting half a million plus some work to put the halves together. Divide and conquer turns that observation into an algorithm and into a recurrence. It is the source of the fastest known algorithms for sorting, searching ordered data, multiplying large numbers and matrices, finding the median, and many geometric problems. It also gives the questions of the syllabus a standard form: write the recurrence, then solve it.
Prerequisites
You need recursion and the call stack (recursion and the call stack) and the methods for solving recurrences (solving recurrences).
Precise definitions
Divide and conquer solves a problem of size in three steps:
- Divide the input into smaller subproblems of the same kind, each of size about (the subproblems are independent: they do not share work).
- Conquer each subproblem recursively. A subproblem smaller than a fixed constant (the base case) is solved directly.
- Combine the answers of the subproblems into the answer of the whole problem.
The recurrence. If dividing and combining together cost , the running time satisfies With the method is sometimes called decrease and conquer (binary search, fast exponentiation): only one subproblem is solved.
Correctness is proved by strong induction on : the base case is correct; if the recursive calls are correct for all sizes less than , and the combine step turns correct answers of the parts into a correct answer of the whole, then the call for is correct.
Depth and space. If the problem is cut into pieces of size the recursion depth is , so the stack uses frames (plus whatever the algorithm stores in each frame, for example a temporary array in merge sort).
How it works
The skeleton.
solve(problem P of size n):
if n is small: return the answer directly /* base case */
split P into a parts of size about n / b /* divide: cost counted in f(n) */
for each part: answer[i] = solve(part) /* conquer: a recursive calls */
return combine(answer[0..a-1]) /* combine: cost counted in f(n) */An example: maximum and minimum. To find the largest and the smallest of keys, a plain scan that updates both uses comparisons. Divide and conquer: split the array into two halves, find the maximum and minimum of each, then compare the two maxima and the two minima (two comparisons). Two keys need only one comparison. The recurrence for the number of comparisons is and for a power of two the solution is (check: , , ). The code below is what the animation runs; only the divide and conquer function is shown and the array is .
Maximum and minimum by divide and conquer
Real runC source
Array a
Counts
Blue = the position this step is working on, amber = a position being compared, red = positions just swapped, green = finished or in its final place, a filled blue cell = a value just placed, faded = not part of the structure at this step.
Step 1
Find the largest and smallest of 8 keys.
The table gives the measured comparison counts for next to the plain scan.
Maximum and minimum: scanning versus divide and conquer: start
Computed by a script, not typed| n | plain scan: 2(n - 1) | divide and conquer (measured) | 3n/2 - 2 | saved |
|---|---|---|---|---|
| 2 | ||||
| 4 | ||||
| 8 | ||||
| 16 | ||||
| 32 | ||||
| 64 |
Step 0
Both methods find the largest and the smallest of n keys, n a power of two. The divide and conquer count is measured by running max_min (the code of the animation) with a counter.
Two remarks. First, for that is not a power of two the same code can use a comparison more than : for it uses , not , so the formula is for powers of two. Second, the saving does not come from recursion as such: comparing the keys in pairs first and then taking the larger of each pair for the maximum and the smaller for the minimum gives the same with a loop. Divide and conquer is a way to find such an idea, not always the only way to write it.
Where the work is done. The recursion tree of has subproblems of size on level , so level costs , and there are levels. The Master theorem (solving recurrences) compares with (the number of leaves):
- grows polynomially slower than : the levels cost more and more going down, the leaves dominate, . Example: maximum and minimum, , , : . Strassen, Karatsuba.
- grows like : every level costs about the same, (for ). Examples: merge sort, binary search (, : ).
- grows polynomially faster (with the regularity condition): the root dominates, . Example: .
For the balanced case the table of a recursion tree makes it visible: every level costs .
Recursion tree of T(n) = 2T(n/2) + n: start
Computed by a script, not typed| level | subproblems | size of each | cost of one | cost of the level |
|---|---|---|---|---|
| 0 | ||||
| 1 | ||||
| 2 | ||||
| 3 | ||||
| 4 | ||||
| 5 | ||||
| 6 | ||||
| 7 | ||||
| 8 | ||||
| 9 | ||||
| 10 |
Step 0
Every level of the tree of T(n) = 2T(n/2) + n costs the same, n: the number of subproblems doubles while their size halves. Here n = 1024, so there are 11 levels.
How the choices change the answer. Reducing the number of subproblems matters more than the cost of combining: is but is (Karatsuba's idea), and is against (Strassen's idea). Both are on the next page.
Unbalanced division. Splitting into very unequal parts makes the tree deep: is (the worst case of quicksort), is still because the depth stays logarithmic.
When divide and conquer is the wrong tool. The subproblems must be independent. The recursion for Fibonacci numbers, , makes two calls that both compute and so on: the running time is exponential, with . When subproblems overlap, remember their answers: that is dynamic programming.
Worked examples
EX 1Divide and conquer that gains nothing
EasySumming numbers by splitting in halves, adding the two partial sums: , which is by case 1 (leaves dominate: against ). It is exactly the work of a loop (every number is added once), but it uses a stack of depth instead of a single variable, and its two halves could be computed on different processors. Recursion does not make an algorithm faster by itself.
EX 2Count the comparisons of max and min
EasyFor : the array is cut into , each into . There are four pairs, one comparison each (), then two combine steps at the middle level of two comparisons each (), then one at the top of two comparisons (): , against for the plain scan. The animation shows the running total ending at .
EX 3The number of subproblems decides the exponent
MediumCompare , and . In each case , and is , and . The first two have polynomially smaller than the leaf count, so and (leaves dominate); the third has equal to it, so . Going from to subproblems lowers the exponent from to ; going to removes the polynomial gap altogether.
EX 4Overlapping subproblems
MediumThe call tree of fib(n) = fib(n - 1) + fib(n - 2) has calls, growing like : . A divide and conquer on independent halves would give a polynomial bound; here the two calls repeat each other's work. Storing the answers (memoization) brings it down to additions (dynamic programming).
EX 5Recurrences of unusual shape
GATE level(a) : the Master theorem does not apply; the tree has depth between and and every level costs at most , so . (b) : divide by and write : , which is , so (confirmed numerically). (c) An algorithm splits the input into three parts of size and combines in : . (d) Three parts of size and combine in : . Always compute first.
Cheat box
Cheat box
- Divide, conquer (independent subproblems), combine. .
- Compare with : smaller (leaves) ; equal ; larger with regularity (root) .
- Max and min by divide and conquer: , comparisons for (against ).
- Depth ; stack space for balanced splits, for a chain.
- Fewer subproblems is better than a cheaper combine: (Karatsuba), (Strassen).
- Overlapping subproblems dynamic programming; is exponential.
Traps and GATE patterns
Connections
Unlocks
Across subjects
The simplest instance with one subproblem is binary search. The classic two-subproblem algorithms are merge sort and quicksort, and the standard collection of applications (Strassen, Karatsuba, closest pair, maximum subarray, selection) is on the next page, divide and conquer applications. Recurrences are solved by the methods of solving recurrences; repeating subproblems lead to dynamic programming.
PYQs
5 previous year questions from your book. I re-solved each one myself (1 by running or simulating, 4 by reasoning only); the book's key is shown next to my answer. All PYQs
Exponentiation is a heavily used operation in public key cryptography. Which of the following options is the tightest upper bound on the number of multiplications required to compute bnmod m,0≤b , n≤m?
- (a)O(logn)
- (b)O(√n )
- (c)O(n/logn)
- (d)O(n)
There are n unsorted arrays: A 1 , A 2 , ..., A n . Assume that n is odd. Each of A 1 , A 2 , ..., A n contains n distinct elements. There are no common elements between any two arrays. The worst-case time complexity of computing the median of the medians of A 1 , A 2 , ..., A n is
- (a)O(n)
- (b)O(n log n)
- (c)O(n2)
- (d)Ω(n2 log n)
Consider the following table: Algorithms Design Paradigms P Kruskal i Divide and Conquer Q Quicksort ii Greedy R Floyd- Warshall iii Dynamic Programming
- (a)P-(ii), Q-(iii), R-(i)
- (b)P-(iii), Q-(i), R-(ii)
- (c)P-(ii), Q-(i), R-(iii)
- (d)P-(i), Q-(ii), R-(iii)
Given below are some algorithms, and some algorithm design paradigms. List - I P. Dijkstra's Shortest Path Q. Floyd-Warshall algorithm to compute all pairs shortest path R. Binary search on a sorted array S. Backtracking search on a graph List – II i. Divide and Conquer ii. Dynamic Programming iii. Greedy design iv. Depth-first search v. Breadth-first search Match the above algorithms on the left to the corresponding design paradigm they follow.
- (a)P-i, Q-iii, R-i, S-v.
- (b)P-iii, Q-iii, R-i, S-v.
- (c)P-iii, Q-ii, R-i, S-iv.
- (d)P-iii, Q-ii, R-i, S-v.
Match the following: (P) Prim's algorithm for minimum spanning tree (i) Backtracking (Q) Floyd-Warshall algorithm for all pairs shortest paths (ii) Greedy method (R) Mergesort (iii) Dynamic programming (S) Hamiltonian circuit (iv) Divide and conquer
- (a)P-iii, Q-ii, R-iv, S-i
- (b)P-i, Q-ii, R-iv, S-iii
- (c)P-ii, Q-iii, R-iv, S-i
- (d)P-ii, Q-i, R-iii, S-iv
Self-check
Question 1
How many comparisons does divide and conquer max-and-min use on keys?
Show answerHide
(against for the plain scan).
Question 2
Solve and .
Show answerHide
. The first has : equal, . The second has : smaller, .
Question 3
Why is divide and conquer a poor way to compute Fibonacci numbers by ?
Show answerHide
The two subproblems overlap, so the same values are recomputed many times: exponential time. Memoization (dynamic programming) fixes this.
Question 4
What is the recursion depth, and hence the stack use, of a balanced divide and conquer on keys?
Show answerHide
About levels, so a stack of about frames.
Revision card
One-screen revision
- ; compare with (leaves / every level / root).
- Max and min: ( a power of two).
- Subproblems must be independent; overlap DP. Depth .
- Fewer subproblems, smaller exponent: , .
- Unbalanced: ; .
References
- ·Cormen, Leiserson, Rivest, Stein, Introduction to Algorithms (3rd edition), Chapter 4 Divide-and-Conquer (4 intro, 4.1 maximum subarray) and 2.3.1 The divide-and-conquer approach
- ·Kleinberg and Tardos, Algorithm Design, Chapter 5 Divide and Conquer (5 intro)
- ·Dasgupta, Papadimitriou, Vazirani, Algorithms, Chapter 2 Divide-and-conquer algorithms
Verification
The animation is a real run of the C code shown; the comparison counts of the table were measured by running that code with a counter for n = 2 to 64 and equal 3n/2 - 2 (scripts/gen-algo-sort-traces.py), and the recurrence T(n) = 2T(n/2) + 2 was solved exactly in an independent Python check, which also shows that the count is not 3n/2 - 2 for n = 6. The solution of T(n) = sqrt(n) T(sqrt n) + n was confirmed numerically. The Master theorem facts are those of the recurrence page. Textbook chapter references are from memory. SECOND PASS (2026-11-02, done by the assistant, not a human check): every Self-check question was solved before the printed answer was read and all agreed; the definitions, worked examples, cheat box and traps were read claim by claim and the numbers re-derived by hand or by a small script (loop counts, recurrences, merge and quicksort counts and averages, hash probes and load-factor formulas, DP tables, MST and shortest-path runs, Floyd matrices); long C programs were not re-read line by line (they are compiled and compared by npm run verify:c, and their printed outputs were checked against the prose); tables and animations are machine output and were read, not re-run; No other defect found. Status raised to self-checked; verified needs a textbook or human check.
last verified: never
My notes (saved in this browser)