feat(patterns): pattern taxonomy + is_optimal
This commit is contained in:
97
backend/data/patterns/divide-and-conquer.yaml
Normal file
97
backend/data/patterns/divide-and-conquer.yaml
Normal file
@@ -0,0 +1,97 @@
|
||||
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
|
||||
Reference in New Issue
Block a user