98 lines
2.9 KiB
YAML
98 lines
2.9 KiB
YAML
name: Divide and Conquer
|
|
slug: divide-and-conquer
|
|
difficulty_level: 3
|
|
pattern_type: algorithm
|
|
display_order: 23
|
|
|
|
description: >
|
|
Break a problem into smaller subproblems, solve them independently,
|
|
and combine results. Unlike DP, subproblems don't overlap.
|
|
|
|
when_to_use: |
|
|
- Problems naturally split into independent halves
|
|
- Merge sort style combining
|
|
- Finding kth element efficiently
|
|
- Tree-structured recursion without memoization needs
|
|
|
|
metaphor: |
|
|
Imagine sorting a deck of cards by splitting it in half, having two
|
|
friends each sort their half, then merging the sorted halves. Each
|
|
friend can recursively split their half too. The key: halves are
|
|
independent and combining sorted halves is easy.
|
|
|
|
core_concept: |
|
|
Three steps:
|
|
1. **Divide:** Split problem into smaller subproblems
|
|
2. **Conquer:** Solve subproblems recursively (base case when trivial)
|
|
3. **Combine:** Merge subproblem solutions into final answer
|
|
|
|
Time complexity often follows: T(n) = aT(n/b) + O(n^c)
|
|
Solved by Master Theorem.
|
|
|
|
code_template: |
|
|
def merge_sort(arr: list[int]) -> list[int]:
|
|
"""Classic divide and conquer example."""
|
|
if len(arr) <= 1:
|
|
return arr
|
|
|
|
# Divide
|
|
mid = len(arr) // 2
|
|
left = merge_sort(arr[:mid])
|
|
right = merge_sort(arr[mid:])
|
|
|
|
# Combine (merge)
|
|
return merge(left, right)
|
|
|
|
def merge(left: list[int], right: list[int]) -> list[int]:
|
|
result = []
|
|
i = j = 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
|
|
|
|
recognition_signals:
|
|
- "merge sort"
|
|
- "find kth largest/smallest"
|
|
- "count inversions"
|
|
- "closest pair of points"
|
|
- "problems on sorted arrays that can be split"
|
|
|
|
common_mistakes:
|
|
- title: Confusing with Dynamic Programming
|
|
description: |
|
|
D&C subproblems are independent. DP subproblems overlap and need
|
|
memoization. Using D&C on overlapping subproblems causes exponential time.
|
|
fix: |
|
|
Check: Do subproblems share computation? If yes, use DP. If no, use D&C.
|
|
|
|
- title: Inefficient Combine Step
|
|
description: |
|
|
If combining takes O(n^2), overall might not be better than brute force.
|
|
fix: |
|
|
Design O(n) or O(n log n) combine step. Merge sort's merge is O(n).
|
|
|
|
variations:
|
|
- name: Merge Sort
|
|
description: Sort by splitting, sorting halves, merging
|
|
example: "sort-an-array"
|
|
- name: Quick Select
|
|
description: Find kth element by partitioning
|
|
example: "kth-largest-element-in-an-array"
|
|
- name: Count Inversions
|
|
description: Count pairs where larger element precedes smaller
|
|
example: "count-of-smaller-numbers-after-self"
|
|
|
|
related_patterns:
|
|
- binary-search
|
|
- dynamic-programming
|
|
|
|
prerequisite_patterns:
|
|
- binary-search
|