Divide and Conquer
๐ Concept
Divide and Conquer breaks a problem into smaller sub-problems, solves each independently, then combines the results. It's the foundation of merge sort, quicksort, binary search, and many efficient algorithms.
Three steps:
- Divide โ Split the problem into smaller sub-problems
- Conquer โ Solve each sub-problem recursively (or directly if small enough)
- Combine โ Merge the sub-solutions into the final answer
Classic D&C algorithms:
- Merge Sort โ Divide array in half, sort each, merge โ O(n log n)
- Quick Sort โ Partition around pivot, sort each side โ O(n log n) avg
- Binary Search โ Divide search space in half โ O(log n)
- Karatsuba Multiplication โ Multiply large numbers faster โ O(n^1.585)
- Closest Pair of Points โ Find nearest pair โ O(n log n)
- Strassen's Matrix Multiplication โ Faster matrix multiply โ O(n^2.807)
Master Theorem for analyzing D&C recurrences: T(n) = aT(n/b) + O(nแต)
- If d < log_b(a): T(n) = O(n^(log_b(a)))
- If d = log_b(a): T(n) = O(nแต log n)
- If d > log_b(a): T(n) = O(nแต)
๐ Real-world analogy: Organizing a library. Split all books into A-M and N-Z (divide), organize each group (conquer), then put them together on the shelf (combine). Each group can be further split.
๐ป Code Example
1// MERGE SORT โ Classic D&C2function mergeSort(arr: number[]): number[] {3 if (arr.length <= 1) return arr; // Base case45 const mid = Math.floor(arr.length / 2);6 const left = mergeSort(arr.slice(0, mid)); // Divide7 const right = mergeSort(arr.slice(mid)); // Divide89 return merge(left, right); // Combine10}1112function merge(left: number[], right: number[]): number[] {13 const result: number[] = [];14 let i = 0, j = 0;15 while (i < left.length && j < right.length) {16 if (left[i] <= right[j]) result.push(left[i++]);17 else result.push(right[j++]);18 }19 return [...result, ...left.slice(i), ...right.slice(j)];20}2122// COUNT INVERSIONS โ Merge sort modification23function countInversions(arr: number[]): number {24 if (arr.length <= 1) return 0;25 const mid = Math.floor(arr.length / 2);26 const left = arr.slice(0, mid);27 const right = arr.slice(mid);28 let count = countInversions(left) + countInversions(right);2930 let i = 0, j = 0, k = 0;31 while (i < left.length && j < right.length) {32 if (left[i] <= right[j]) { arr[k++] = left[i++]; }33 else {34 arr[k++] = right[j++];35 count += left.length - i; // All remaining left elements form inversions36 }37 }38 while (i < left.length) arr[k++] = left[i++];39 while (j < right.length) arr[k++] = right[j++];40 return count;41}4243// MAXIMUM SUBARRAY โ D&C approach (Kadane's is simpler, but this shows D&C)44function maxSubarrayDC(arr: number[], lo: number, hi: number): number {45 if (lo === hi) return arr[lo];4647 const mid = Math.floor((lo + hi) / 2);48 const leftMax = maxSubarrayDC(arr, lo, mid);49 const rightMax = maxSubarrayDC(arr, mid + 1, hi);50 const crossMax = maxCrossing(arr, lo, mid, hi);5152 return Math.max(leftMax, rightMax, crossMax);53}5455function maxCrossing(arr: number[], lo: number, mid: number, hi: number): number {56 let leftSum = -Infinity, sum = 0;57 for (let i = mid; i >= lo; i--) { sum += arr[i]; leftSum = Math.max(leftSum, sum); }58 let rightSum = -Infinity; sum = 0;59 for (let i = mid + 1; i <= hi; i++) { sum += arr[i]; rightSum = Math.max(rightSum, sum); }60 return leftSum + rightSum;61}6263// POWER โ D&C O(log n)64function power(base: number, exp: number): number {65 if (exp === 0) return 1;66 if (exp < 0) return 1 / power(base, -exp);67 const half = power(base, Math.floor(exp / 2));68 return exp % 2 === 0 ? half * half : half * half * base;69}
๐๏ธ Practice Exercise
Practice Problems:
- Implement merge sort and count the number of comparisons
- Count inversions in an array using modified merge sort
- Find maximum subarray sum using divide and conquer
- Implement power(x, n) in O(log n) using D&C
- Find the closest pair of points in a 2D plane
- Multiply two large numbers using Karatsuba algorithm (conceptual)
- Find kth largest element using quickselect (average O(n))
- Construct a binary tree from preorder and inorder traversal
- Find the majority element using D&C (Boyer-Moore is better, but practice D&C)
- Skyline problem โ merge skylines from two halves
โ ๏ธ Common Mistakes
Not identifying the base case โ D&C needs a base case for the smallest sub-problem
Inefficient combining step โ the combine step can dominate complexity if not optimized
Creating too many new arrays (memory overhead) โ in-place D&C is often preferred
Using D&C when a simpler approach exists โ Kadane's O(n) beats D&C O(n log n) for max subarray
Not applying the Master Theorem to analyze complexity โ essential for understanding D&C performance
๐ผ Interview Questions
๐ค Mock Interview
Practice a live interview for Divide and Conquer