Divide and Conquer
Comprehensive guide to Divide and Conquer, including Master Theorem, Akra-Bazzi, Strassen's Algorithm, and advanced applications
Fundamental Concepts
Definition
Divide and Conquer is an algorithm design paradigm based on multi-branched recursion. A divide and conquer algorithm works by recursively breaking down a problem into two or more sub-problems of the same or related type, until these become simple enough to be solved directly. The solutions to the sub-problems are then combined to give a solution to the original problem.
The Three Steps
- Divide: Break the problem into subproblems that are smaller instances of the same problem.
- Conquer: Solve the subproblems recursively. If the subproblem sizes are small enough (base case), solve them directly.
- Combine: Combine the solutions to the subproblems into the solution for the original problem.
Recurrence Relations
The running time of a divide and conquer algorithm is naturally described by a recurrence relation:
where:
- is the size of the problem.
- is the number of subproblems in the recursion.
- is the size of each subproblem.
- is the cost of dividing the problem and combining the results.
Recurrence Analysis Methods
1. The Master Theorem
The Master Theorem provides a "cookbook" solution for recurrences of the form where .
Compare with (the "watershed" function):
| Case | Condition | Solution | Intuition |
|---|---|---|---|
| 1. Leaf-heavy | for some | The cost is dominated by the leaves (base cases). | |
| 2. Balanced | The cost is evenly distributed across levels. | ||
| 3. Root-heavy | and regularity cond.* | The cost is dominated by the root (divide/combine). |
*Regularity condition: for some constant and sufficiently large .
Examples:
- Binary Search: . . . Case 2 applies. .
- Merge Sort: . . . Case 2 applies. .
- Strassen's: . . . Since , Case 1 applies. .
2. The Akra-Bazzi Method
A generalization of the Master Theorem for recurrences where subproblems have unequal sizes. For , the solution is:
where is the unique real root of .
Example: . Solve . Then compute the integral.
Essential Algorithms & Implementations
Merge Sort
The quintessential divide and conquer sorting algorithm. Stable and guaranteed .
def merge_sort(arr):
# Base case
if len(arr) <= 1:
return arr
# Divide
mid = len(arr) // 2
left = merge_sort(arr[:mid])
right = merge_sort(arr[mid:])
# Combine
return merge(left, right)
def merge(left, right):
sorted_arr = []
i = j = 0
# Merge two sorted arrays
while i < len(left) and j < len(right):
if left[i] <= right[j]:
sorted_arr.append(left[i])
i += 1
else:
sorted_arr.append(right[j])
j += 1
sorted_arr.extend(left[i:])
sorted_arr.extend(right[j:])
return sorted_arrStrassen's Matrix Multiplication
Reduces matrix multiplication complexity from to .
Key Idea: Divide matrices into four submatrices. Instead of 8 multiplications, compute 7 products () of linear combinations of submatrices.
The 7 Products:
The Combination:
Karatsuba Algorithm (Fast Multiplication)
Multiplies two -digit numbers in instead of . Let and . Then .
Trick: Instead of computing (4 muls), compute:
Advanced Techniques
1. Meet-in-the-Middle
Technique to reduce complexity from exponential to . Problem: Subset Sum - Given set , is there a subset summing to ? Approach:
- Split into two halves and of size .
- Generate all subset sums for () and ().
- Sort .
- For each sum , binary search for in .
2. Divide and Conquer on Trees (Centroid Decomposition)
Used for path problems on trees (e.g., count paths with length ).
- Find the centroid of the tree (node that splits tree into components of size ).
- Process paths passing through the centroid.
- Recursively process subtrees (removing centroid). Complexity: because tree height becomes .
3. Square Root Decomposition (Mo's Algorithm)
While not strictly recursive D&C, it divides the problem into blocks of size . Use: Range queries on arrays where updates/queries can be ordered efficiently.
Interview Problem Types
Type 1: Modified Binary Search
| Given | Find | Approach |
|---|---|---|
| Rotated sorted array | Specific element | Compare mid with start to determine which half is sorted, then standard binary search logic. |
| Array where elements increase then decrease (Bitonic) | Peak element | Binary search comparing mid and mid+1. |
| Two sorted arrays | Median of combined | Binary search on partition of smaller array to ensure left half elements right half. |
Type 2: Counting Inversions
| Given | Find | Approach |
|---|---|---|
| Array of integers | Number of pairs such that and | Modified Merge Sort. During merge step, if , then and all remaining in left subarray form inversions with . |
Type 3: Closest Pair of Points
| Given | Find | Approach |
|---|---|---|
| Set of 2D points | Pair with smallest Euclidean distance | Sort by X. Divide into left/right. Solve recursively. Combine by checking "strip" of width around center. |
Type 4: Maximum Subarray Sum
| Given | Find | Approach |
|---|---|---|
| Array of integers (pos/neg) | Contiguous subarray with largest sum | Max is either in left half, right half, or crossing midpoint. Compute crossing sum in . (Note: Kadane's is , but D&C is ). |
Common Pitfalls
Pitfall 1: Off-by-One Errors
Wrong: mid = (left + right) / 2 (Can overflow in languages like C++, though Python handles large ints).
Correct: mid = left + (right - left) // 2.
Wrong: right = mid vs right = mid - 1.
Check: Is the search space inclusive [left, right] or exclusive [left, right)?
Pitfall 2: Base Cases
Wrong: Forgetting to handle empty arrays or single-element arrays in recursion.
Correct: Always check if len(arr) <= 1 (or similar) at the start.
Pitfall 3: Overhead of Recursion
Wrong: Using D&C for very small inputs where iterative methods or insertion sort might be faster due to stack overhead. Correct: Hybrid sort (Timsort) uses Insertion Sort for small blocks.
Pitfall 4: Repeated Work
Wrong: Solving the same subproblem multiple times (e.g., Fibonacci). Correct: This implies Dynamic Programming, not pure Divide and Conquer.
Quick Reference
- Master Theorem: . Compare vs .
- Merge Sort: time, space. Stable.
- Quick Sort: avg, worst. space. Unstable.
- Binary Search: .
- Strassen's: .
- Karatsuba: .
- Closest Pair: .
Practice Problem Categories
- Merge Sort Variations: Count inversions, Count of range sums, Reverse pairs.
- Binary Search on Answer: Minimize max distance, Painter's partition problem, Aggressive cows.
- Matrix Exponentiation: Compute -th Fibonacci in .
- Geometric D&C: Closest pair, Convex Hull (Merge step).
- Tree Decompositions: Centroid decomposition problems.