λΆν μ 볡 (Divide and Conquer)
λΆν μ 볡 (Divide and Conquer)¶
κ°μ¶
λΆν μ 볡μ ν° λ¬Έμ λ₯Ό μμ λΆλΆ λ¬Έμ λ‘ λλκ³ , κ°κ°μ ν΄κ²°ν ν κ²°ν©νλ μκ³ λ¦¬μ¦ μ€κ³ κΈ°λ²μ λλ€.
λͺ©μ°¨¶
1. λΆν μ 볡 κ°λ ¶
κΈ°λ³Έ λ¨κ³¶
λΆν μ 볡 3λ¨κ³:
1. λΆν (Divide)
- λ¬Έμ λ₯Ό λ μμ λΆλΆ λ¬Έμ λ‘ λλ
2. μ 볡 (Conquer)
- λΆλΆ λ¬Έμ λ₯Ό μ¬κ·μ μΌλ‘ ν΄κ²°
- μΆ©λΆν μμΌλ©΄ μ§μ ν΄κ²°
3. κ²°ν© (Combine)
- λΆλΆ λ¬Έμ μ ν΄λ₯Ό ν©μ³ μλ λ¬Έμ μ ν΄ κ΅¬μ±
μκ°ν¶
[λ¬Έμ ]
/ \
[λΆλΆ1] [λΆλΆ2]
/ \ / \
[a] [b] [c] [d]
\ / \ /
[λΆλΆ1] [λΆλΆ2]
\ /
[ν΄λ΅]
λΆν μ 볡 vs DP¶
ββββββββββββββββββ¬ββββββββββββββββββ¬ββββββββββββββββββ
β β λΆν μ 볡 β DP β
ββββββββββββββββββΌββββββββββββββββββΌββββββββββββββββββ€
β λΆλΆ λ¬Έμ β μλ‘ λ
립 β μ€λ³΅ μ‘΄μ¬ β
β μ μ₯ β λΆνμ β λ©λͺ¨μ΄μ μ΄μ
β
β μμ β λ³ν©μ λ ¬ β νΌλ³΄λμΉ β
ββββββββββββββββββ΄ββββββββββββββββββ΄ββββββββββββββββββ
2. λ³ν© μ λ ¬ (Merge Sort)¶
μ리¶
1. λ°°μ΄μ λ°μΌλ‘ λλ
2. κ° λΆλΆμ μ¬κ·μ μΌλ‘ μ λ ¬
3. μ λ ¬λ λ λΆλΆμ λ³ν©
[38, 27, 43, 3, 9, 82, 10]
β λΆν
[38, 27, 43, 3] [9, 82, 10]
β β
[38, 27] [43, 3] [9, 82] [10]
β β β β
[38][27] [43][3] [9][82] [10]
β β β β
[27, 38] [3, 43] [9, 82] [10]
β β
[3, 27, 38, 43] [9, 10, 82]
β κ²°ν©
[3, 9, 10, 27, 38, 43, 82]
ꡬν¶
// C
void merge(int arr[], int left, int mid, int right) {
int n1 = mid - left + 1;
int n2 = right - mid;
int L[n1], R[n2];
for (int i = 0; i < n1; i++) L[i] = arr[left + i];
for (int i = 0; i < n2; i++) R[i] = arr[mid + 1 + i];
int i = 0, j = 0, k = left;
while (i < n1 && j < n2) {
if (L[i] <= R[j]) arr[k++] = L[i++];
else arr[k++] = R[j++];
}
while (i < n1) arr[k++] = L[i++];
while (j < n2) arr[k++] = R[j++];
}
void mergeSort(int arr[], int left, int right) {
if (left < right) {
int mid = left + (right - left) / 2;
mergeSort(arr, left, mid);
mergeSort(arr, mid + 1, right);
merge(arr, left, mid, right);
}
}
def merge_sort(arr):
if len(arr) <= 1:
return arr
mid = len(arr) // 2
left = merge_sort(arr[:mid])
right = merge_sort(arr[mid:])
return merge(left, right)
def merge(left, right):
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
볡μ‘λ λΆμ¶
T(n) = 2T(n/2) + O(n)
λΆν : O(1)
μ 볡: 2 Γ T(n/2)
κ²°ν©: O(n)
λ§μ€ν° μ 리:
a = 2, b = 2, f(n) = n
n^(log_b(a)) = n^1 = n
f(n) = Ξ(n)
β T(n) = Ξ(n log n)
곡κ°: O(n) - μμ λ°°μ΄
3. ν΅ μ λ ¬ (Quick Sort)¶
μ리¶
1. νΌλ² μ ν
2. νΌλ²λ³΄λ€ μμ μμλ μΌμͺ½, ν° μμλ μ€λ₯Έμͺ½
3. κ° λΆλΆμ μ¬κ·μ μΌλ‘ μ λ ¬
[5, 3, 8, 4, 2, 7, 1, 6], νΌλ²=5
β νν°μ
[3, 4, 2, 1] [5] [8, 7, 6]
β β
[1, 2, 3, 4] [6, 7, 8]
β
[1, 2, 3, 4, 5, 6, 7, 8]
ꡬν¶
// C++
int partition(vector<int>& arr, int low, int high) {
int pivot = arr[high];
int i = low - 1;
for (int j = low; j < high; j++) {
if (arr[j] < pivot) {
swap(arr[++i], arr[j]);
}
}
swap(arr[i + 1], arr[high]);
return i + 1;
}
void quickSort(vector<int>& arr, int low, int high) {
if (low < high) {
int pi = partition(arr, low, high);
quickSort(arr, low, pi - 1);
quickSort(arr, pi + 1, high);
}
}
def quick_sort(arr, low, high):
if low < high:
pi = partition(arr, low, high)
quick_sort(arr, low, pi - 1)
quick_sort(arr, pi + 1, high)
def partition(arr, low, high):
pivot = arr[high]
i = low - 1
for j in range(low, high):
if arr[j] < pivot:
i += 1
arr[i], arr[j] = arr[j], arr[i]
arr[i + 1], arr[high] = arr[high], arr[i + 1]
return i + 1
볡μ‘λ λΆμ¶
νκ· : T(n) = 2T(n/2) + O(n) = O(n log n)
μ΅μ
: T(n) = T(n-1) + O(n) = O(nΒ²)
(μ΄λ―Έ μ λ ¬λ λ°°μ΄ + 첫/λ§μ§λ§ νΌλ²)
곡κ°: O(log n) - μ¬κ· μ€ν
4. μ΄μ§ νμ (Binary Search)¶
λΆν μ 볡 κ΄μ ¶
λ¬Έμ : μ λ ¬λ λ°°μ΄μμ target μ°ΎκΈ°
λΆν : μ€κ°κ° κΈ°μ€μΌλ‘ μΌμͺ½/μ€λ₯Έμͺ½ λλ
μ 볡: targetμ΄ μλ μͺ½λ§ νμ
κ²°ν©: νμ μμ (μ°ΎμΌλ©΄ λ°ν)
[1, 3, 5, 7, 9, 11, 13], target=9
β
[7] μ€κ°κ°
7 < 9
β
μ€λ₯Έμͺ½ νμ: [9, 11, 13]
β
[11] μ€κ°κ°
11 > 9
β
μΌμͺ½ νμ: [9]
β
μ°Ύμ!
μ¬κ· ꡬν¶
// C++
int binarySearchRecursive(const vector<int>& arr, int left, int right, int target) {
if (left > right) return -1;
int mid = left + (right - left) / 2;
if (arr[mid] == target) return mid;
if (arr[mid] > target) return binarySearchRecursive(arr, left, mid - 1, target);
return binarySearchRecursive(arr, mid + 1, right, target);
}
def binary_search_recursive(arr, left, right, target):
if left > right:
return -1
mid = (left + right) // 2
if arr[mid] == target:
return mid
if arr[mid] > target:
return binary_search_recursive(arr, left, mid - 1, target)
return binary_search_recursive(arr, mid + 1, right, target)
5. κ±°λμ κ³± (Exponentiation)¶
λ¨μ λ°©λ² vs λΆν μ 볡¶
a^n κ³μ°
λ¨μ: a Γ a Γ a Γ ... Γ a (nλ²) β O(n)
λΆν μ 볡:
a^n = a^(n/2) Γ a^(n/2) (nμ΄ μ§μ)
a^n = a^(n/2) Γ a^(n/2) Γ a (nμ΄ νμ)
β O(log n)
μ: 2^10
= 2^5 Γ 2^5
= (2^2 Γ 2^2 Γ 2) Γ (2^2 Γ 2^2 Γ 2)
= ((2 Γ 2)^2 Γ 2)^2
ꡬν¶
// C - μ¬κ·
long long power(long long a, int n) {
if (n == 0) return 1;
if (n == 1) return a;
long long half = power(a, n / 2);
if (n % 2 == 0) {
return half * half;
} else {
return half * half * a;
}
}
// C - λ°λ³΅ (λΉνΈ μ°μ°)
long long powerIterative(long long a, int n) {
long long result = 1;
while (n > 0) {
if (n & 1) { // nμ΄ νμ
result *= a;
}
a *= a;
n >>= 1;
}
return result;
}
def power(a, n):
if n == 0:
return 1
if n == 1:
return a
half = power(a, n // 2)
if n % 2 == 0:
return half * half
else:
return half * half * a
# λͺ¨λλ¬ κ±°λμ κ³± (ν° μ)
def power_mod(a, n, mod):
result = 1
a %= mod
while n > 0:
if n & 1:
result = (result * a) % mod
a = (a * a) % mod
n >>= 1
return result
νλ ¬ κ±°λμ κ³±¶
νΌλ³΄λμΉλ₯Ό O(log n)μ κ³μ°
[F(n+1)] [1 1]^n [F(1)]
[F(n) ] = [1 0] Γ [F(0)]
νλ ¬ κ±°λμ κ³±μ λΆν μ 볡μΌλ‘!
def matrix_mult(A, B, mod=10**9+7):
return [
[(A[0][0]*B[0][0] + A[0][1]*B[1][0]) % mod,
(A[0][0]*B[0][1] + A[0][1]*B[1][1]) % mod],
[(A[1][0]*B[0][0] + A[1][1]*B[1][0]) % mod,
(A[1][0]*B[0][1] + A[1][1]*B[1][1]) % mod]
]
def matrix_power(M, n, mod=10**9+7):
if n == 1:
return M
if n % 2 == 0:
half = matrix_power(M, n // 2, mod)
return matrix_mult(half, half, mod)
else:
return matrix_mult(M, matrix_power(M, n - 1, mod), mod)
def fibonacci(n):
if n <= 1:
return n
M = [[1, 1], [1, 0]]
result = matrix_power(M, n)
return result[1][0]
6. μ°μ΅ λ¬Έμ ¶
λ¬Έμ 1: λ°°μ΄μ μμ μ κ°μ¶
i < jμ΄κ³ arr[i] > arr[j]μΈ μμ κ°μ
ννΈ
λ³ν© μ λ ¬ κ³Όμ μμ μΉ΄μ΄νΈμ λ΅ μ½λ
def count_inversions(arr):
def merge_count(arr, temp, left, mid, right):
i = left
j = mid + 1
k = left
inv_count = 0
while i <= mid and j <= right:
if arr[i] <= arr[j]:
temp[k] = arr[i]
i += 1
else:
temp[k] = arr[j]
inv_count += (mid - i + 1) # ν΅μ¬!
j += 1
k += 1
while i <= mid:
temp[k] = arr[i]
i += 1
k += 1
while j <= right:
temp[k] = arr[j]
j += 1
k += 1
for i in range(left, right + 1):
arr[i] = temp[i]
return inv_count
def merge_sort_count(arr, temp, left, right):
inv_count = 0
if left < right:
mid = (left + right) // 2
inv_count += merge_sort_count(arr, temp, left, mid)
inv_count += merge_sort_count(arr, temp, mid + 1, right)
inv_count += merge_count(arr, temp, left, mid, right)
return inv_count
n = len(arr)
temp = [0] * n
return merge_sort_count(arr, temp, 0, n - 1)
μΆμ² λ¬Έμ ¶
| λμ΄λ | λ¬Έμ | νλ«νΌ | μ ν |
|---|---|---|---|
| ββ | νλ ¬ μ κ³± | λ°±μ€ | νλ ¬ κ±°λμ κ³± |
| ββ | νΌλ³΄λμΉ μ 6 | λ°±μ€ | νλ ¬ κ±°λμ κ³± |
| ββ | Kλ²μ§Έ μ | λ°±μ€ | Quick Select |
| βββ | λ²λΈ μνΈ | λ°±μ€ | μμ μ |
| βββ | κ°μ₯ κ°κΉμ΄ λ μ | λ°±μ€ | λΆν μ 볡 |
λ§μ€ν° μ 리 (Master Theorem)¶
T(n) = aT(n/b) + f(n) ννμ μ νμ ν΄κ²°
Case 1: f(n) = O(n^(log_b(a) - Ξ΅))
β T(n) = Ξ(n^(log_b(a)))
Case 2: f(n) = Ξ(n^(log_b(a)))
β T(n) = Ξ(n^(log_b(a)) log n)
Case 3: f(n) = Ξ©(n^(log_b(a) + Ξ΅))
β T(n) = Ξ(f(n))
μ:
- λ³ν© μ λ ¬: T(n) = 2T(n/2) + n β O(n log n) [Case 2]
- μ΄μ§ νμ: T(n) = T(n/2) + 1 β O(log n) [Case 2]
- κ±°λμ κ³±: T(n) = T(n/2) + 1 β O(log n) [Case 2]
λ€μ λ¨κ³¶
- 08_Backtracking.md - λ°±νΈλνΉ
μ°Έκ³ μλ£¶
- Introduction to Algorithms (CLRS) - Chapter 4
- Divide and Conquer