stackademic

The leading education platform for anyone with an interest in software development.

Divide and Conquer

Break a problem into independent subproblems, solve them, and combine the results

Overview

Divide and conquer splits a problem into smaller, independent subproblems, solves each recursively, and merges their solutions. It underpins classic algorithms like merge sort, quicksort, and fast exponentiation. When the subproblems are balanced, the running time follows the Master Theorem; merge sort, for example, is O(n log n) time.

Syntax / Usage

The recipe has three steps: divide the input, conquer each half recursively, and combine. The base case handles inputs small enough to solve directly.

def merge_sort(nums):                 # O(n log n) time, O(n) space
    if len(nums) <= 1:                # base case: already sorted
        return nums
    mid = len(nums) // 2
    left = merge_sort(nums[:mid])     # conquer left half
    right = merge_sort(nums[mid:])    # conquer right half
    return merge(left, right)         # combine sorted halves

def merge(left, right):
    result, i, j = [], 0, 0
    while i < len(left) and j < len(right):
        if left[i] <= right[j]:
            result.append(left[i]); i += 1
        else:
            result.append(right[j]); j += 1
    result.extend(left[i:])
    result.extend(right[j:])
    return result

Examples

Sort a list with merge sort:

print(merge_sort([5, 2, 9, 1, 5, 6]))  # [1, 2, 5, 5, 6, 9]

Fast exponentiation halves the exponent each step for O(log n) multiplications:

def power(base, exp):                 # O(log exp) time
    if exp == 0:
        return 1
    half = power(base, exp // 2)
    return half * half * (base if exp % 2 else 1)

print(power(2, 10))  # 1024

Common Mistakes

  • Forgetting the base case, so recursion never terminates.
  • Splitting into subproblems that overlap; that calls for dynamic programming, not pure divide and conquer.
  • Doing expensive work in the combine step and underestimating its cost in the recurrence.
  • Slicing lists in Python repeatedly, which adds hidden O(n) copies per level.
  • Assuming balanced splits; a skewed split (like bad quicksort pivots) degrades to O(n^2).

See Also

algorithms-recursion algorithms-sorting algorithms-binary-search-patterns