RECURSION & SEARCH / ALGORITHM BRIEF

Divide and conquer

Divide and conquer solves a problem by splitting it into independent smaller subproblems of the same type, solving each recursively, and combining their answers.

IntermediatePhase 06 / Topic 6 of 6Mental modelComplexityEdge cases
01

Overview

Divide and conquer solves a problem by splitting it into independent smaller subproblems of the same type, solving each recursively, and combining their answers. Merge sort, quicksort, binary search, fast exponentiation, and closest pair of points all follow this shape.

It differs from dynamic programming because the subproblems do not overlap: each piece is solved once. The cost is analyzed with a recurrence, such as T(n) = 2T(n/2) + O(n) for merge sort, which the Master Theorem solves to O(n log n).

Sorting a huge pile of exam papers

Split the pile between two assistants, who each split theirs again, until everyone holds one paper. Then pairs merge their sorted piles, and the merged piles are merged again until one sorted pile remains.

02

When to use it

  • The input splits naturally into halves or independent parts.
  • The combine step is cheaper than solving the whole problem directly.
  • Counting problems that can be solved during a merge (inversions, reverse pairs).
  • Building balanced structures from sorted data, or tree problems with left and right halves.
03

Problem patterns it solves

Split, solve, merge

Recognize it when: sorting or counting that can happen during a merge.

  • 912. Sort an Array
  • 148. Sort List
  • 315. Count of Smaller Numbers After Self
  • 493. Reverse Pairs
Halving the problem

Recognize it when: each step reduces the problem to one half.

  • 50. Pow(x, n)
  • 704. Binary Search
  • 4. Median of Two Sorted Arrays
Build from halves

Recognize it when: construct a tree or structure from a middle split.

  • 108. Convert Sorted Array to BST
  • 654. Maximum Binary Tree
  • 105. Construct Binary Tree from Preorder and Inorder
Combine results across the split

Recognize it when: the best answer may cross the middle.

  • 53. Maximum Subarray (divide and conquer)
  • 241. Different Ways to Add Parentheses
  • 932. Beautiful Array
Merge k things

Recognize it when: merge lists pairwise in rounds.

  • 23. Merge k Sorted Lists
04

Where it is used in real software

Sorting large datasets

External merge sort splits data into memory-sized chunks, sorts each, and merges them; MapReduce jobs follow the same split-and-combine model.

Fast Fourier Transform

The FFT divides a signal into even and odd parts recursively, powering audio processing, image compression, and fast polynomial multiplication.

Parallel computing

Java's Fork/Join framework and parallel streams split work recursively across CPU cores and combine results.

Computational geometry

Closest pair of points and convex hull algorithms achieve O(n log n) with divide and conquer.

05

Key terms

Divide
Split the input into smaller independent parts.
Conquer
Solve each part recursively; tiny parts are base cases.
Combine
Merge the partial answers into the full answer.
Recurrence
T(n) = aT(n/b) + f(n), describing the total cost.
06

How it works, step by step

  1. 1
    Define the base case

    An empty or single-element input is solved directly.

  2. 2
    Split

    Usually at the middle: mid = (lo + hi) >> 1.

  3. 3
    Solve both halves recursively

    Trust that each returns a correct answer for its half.

  4. 4
    Combine

    Merge sorted halves, sum counts, or compute the answer that crosses the middle.

  5. 5
    Analyze with the Master Theorem

    Compare the number of subproblems with the cost of combining.

07

Counting inversions with merge sort

arr = [2, 4, 1, 3, 5]; an inversion is i < j with arr[i] > arr[j]

Step 1 / 4
MergeLeftRightInversions foundMerged
1[2][4]0[2, 4]
2[3][5]0[3, 5]
3[1][3, 5]0[1, 3, 5]
4[2, 4][1, 3, 5]1 < 2: 2 left elements remain (2 pairs); 3 < 4: 1 remains (1 pair)[1, 2, 3, 4, 5]

NOWMerge: 1 | Left: [2] | Right: [4] | Inversions found: 0 | Merged: [2, 4]

Total inversions = 3: (2,1), (4,1), (4,3). When an element from the right half is placed first, it is smaller than every remaining element in the left half, so all of them form inversions at once. O(n log n) instead of O(n^2).

08

Implementation

function countInversions(arr) {  function sortCount(a) {    if (a.length <= 1) return [a, 0];    const mid = a.length >> 1;    const [left, leftCount] = sortCount(a.slice(0, mid));    const [right, rightCount] = sortCount(a.slice(mid));    const merged = [];    let i = 0, j = 0, cross = 0;    while (i < left.length && j < right.length) {      if (left[i] <= right[j]) merged.push(left[i++]);      else {        merged.push(right[j++]);        cross += left.length - i; // every remaining left element is larger      }    }    return [merged.concat(left.slice(i), right.slice(j)), leftCount + rightCount + cross];  }  return sortCount(arr)[1];} // 50. Pow(x, n): T(n) = T(n/2) + O(1) = O(log n)function myPow(x, n) {  if (n === 0) return 1;  if (n < 0) return 1 / myPow(x, -n);  const half = myPow(x, Math.floor(n / 2));  return n % 2 === 0 ? half * half : half * half * x;} // 23. Merge k Sorted Lists by merging pairs in rounds: O(N log k)function mergeKLists(lists) {  if (!lists.length) return null;  while (lists.length > 1) {    const next = [];    for (let i = 0; i < lists.length; i += 2) next.push(mergeTwo(lists[i], lists[i + 1] ?? null));    lists = next;  }  return lists[0];} function mergeTwo(a, b) {  const dummy = { next: null };  let tail = dummy;  while (a && b) {    if (a.val <= b.val) { tail.next = a; a = a.next; } else { tail.next = b; b = b.next; }    tail = tail.next;  }  tail.next = a ?? b;  return dummy.next;}
09

Complexity and performance

Merge sortO(n log n)

T(n) = 2T(n/2) + O(n).

Binary search / fast powerO(log n)

T(n) = T(n/2) + O(1).

Tree-like recursionO(n)

T(n) = 2T(n/2) + O(1).

SpaceO(log n) to O(n)

Recursion depth plus merge buffers.

10

Trade-offs

Divide and conquer vs DP

If subproblems overlap, divide and conquer recomputes them; add memoization and it becomes DP (as with Different Ways to Add Parentheses).

Recursion overhead

For small inputs, switching to a simple algorithm (insertion sort under ~16 elements) is faster.

Parallelism

Independent subproblems can run in parallel, a big practical advantage.

11

Variants and related techniques

Decrease and conquer

Solve only one smaller subproblem: binary search, quickselect.

Karatsuba and Strassen

Reduce the number of recursive multiplications to beat the naive complexity.

CDQ divide and conquer

Offline technique for problems with three ordering dimensions.

12

Common mistakes

  • Unbalanced splits.

    Fix: Quicksort with bad pivots degrades to O(n^2); split at the middle or randomize.

  • Copying arrays with slice at every level.

    Fix: Pass index ranges and a shared buffer to reduce memory and allocation.

  • Missing the crossing case.

    Fix: When the answer can span both halves (max subarray), compute it explicitly in the combine step.

13

Interview questions

How is divide and conquer different from dynamic programming?

Divide and conquer subproblems are independent and each solved once. DP applies when the same subproblems repeat, so results are cached and reused.

Why is merge sort O(n log n)?

The array halves log n times, and at each level the merges together touch all n elements, so the total work is n x log n.

14

Practice problems

ProblemDifficultyWhat it trains
50. Pow(x, n)MediumHalving recursion.
912. Sort an ArrayMediumMerge sort.
148. Sort ListMediumMerge sort on a list.
241. Different Ways to Add ParenthesesMediumSplit at operators.
23. Merge k Sorted ListsHardPairwise merge rounds.
493. Reverse PairsHardCount during merge.