Algorithms : Divide-and-Conquer
이 포스트에서는 Divide-and-Conquer 알고리즘에 대한 설명을 다룬다.
Divide-and-Conquer
- 개요
문제를 작은 하위 문제들로 분할(divide)하여, 하위 문제들을 재귀적으로 해결(conquer)하고, 결과를 조합(combine)하여 원래 문제의 해를 구하는 상향식 알고리즘 설계 방식
Divide-and-Conquer 일반 구조
- Divide: 문제를 하나 또는 여러 개의 하위 문제로 분할
- Conquer: 각 하위 문제를 재귀적으로 해결
- Combine: 하위 문제의 해를 결합해 원래 문제의 해를 구함
Binay Search(이진 탐색)
- 정렬된 배열에서 원하는 키
x를 탐색하는 알고리즘. - 중간값과 비교 후, 작으면 왼쪽, 크면 오른쪽 하위 배열에서 반복.
- 재귀 버전을 통해 Divide-and-Conquer 방식 예시로 활용.
- 특징:
- Tail Recursion → 반복문으로 쉽게 전환 가능.
- 반복문이 재귀보다 빠르고 메모리 효율적.
- 배열은 주소로 전달되므로 불필요한 복사 피할 수 있음 (
const사용).
- 수식
종료 조건 : \(\frac{n}{2^k} = 1 \Rightarrow k = \log_2 n\) 최종식 : \(T(n) = T(1) + \log_2 n\) 따라서, \(T(n) \leq 1 + \log_2 n = O(\log n)\)
Merge Sort
- 두 개의 정렬된 배열을 병합하는 방식으로 전체 배열 정렬.
- 단계:
- 배열을 2개의 하위 배열로 나눔 (n/2)
- 각 하위 배열을 재귀적으로 정렬
- 정렬된 배열들을 병합
- 비교 횟수: 평균적으로
O(n log n) -
In-place 정렬은 아님 (추가 메모리 사용함).
- 수식
- 종료조건
- k-way Merge Sort는 어떨까?
- 기존 Merge Sort의 시간복잡도
- k-way Merge Sort의 시간복잡도
일반화 시간복잡도 \(O(n \log_k n) = O\left( \frac{n \log n}{\log k} \right)\)
- 따라서 k가 커질수록 로그의 밑이 커져서 전체 복잡도는 작아짐!
하지만 실제로 무조건 k가 크다고 좋은 것은 아님
- Merge 단계에서 k개의 리스트를 병합해야 하므로 더 복잡해짐
- 2-way는 2개의 포인터만 쓰면 되지만
- k-way는 k개의 포인터 + 최소 힙(min-heap)을 써야 효율적
- 캐시/메모리 접근 패턴이 더 복잡해져서 실제 성능이 떨어질 수 있음
- CPU 캐시와 분기 예측 등의 이유로
- 너무 많은 분기로 나누면 오히려 느려질 수 있음
즉 Optimal K의 값은?
이론적으로는 시간복잡도만 본다면 k가 클수록 좋음
실무와 현실에서는 보통 k = 2 또는 3이 가장 빠름.
Quick Sort
- 정렬 기준이 되는 pivot을 선택하고,
- pivot보다 작은 값은 왼쪽,
- 큰 값은 오른쪽에 배치
-
이후 각 부분 배열에 대해 재귀적으로 정렬 수행
-
partition 함수는 각 원소를 순회하며 pivot보다 작으면 왼쪽으로 이동
- 성능:
- 최선/평균:
O(n log n)(pivot이 중간일 때) - 최악:
O(n²)(pivot이 최솟값 또는 최댓값일 때)
- 최선/평균:
-
수식
-
Best Case :
\[= 2T\left(\frac{n}{2}\right) + cn \quad \text{(Divide: 2 subarrays, Combine: partition)}\]O(nlogn)\(T(n) = T\left(\frac{n}{2}\right) + T\left(\frac{n}{2}\right) + cn\)이를 반복하면, \(= 2 \left( 2T\left(\frac{n}{4}\right) + c\frac{n}{2} \right) + cn = 4T\left(\frac{n}{4}\right) + 2cn\)
\[= 8T\left(\frac{n}{8}\right) + 3cn\] \[= 2^k T\left(\frac{n}{2^k}\right) + kcn\] \[기저 조건: \frac{n}{2^k} = 1 \Rightarrow k = \log_2 n\] \[T(n) = nT(1) + cn \log n = O(n \log n)\] -
Worst Case :
\[= T(n-1) + cn = T(n-2) + c(n-1) + cn = T(n-3) + c(n-2) + c(n-1) + cn\] \[= T(1) + c(2 + 3 + \cdots + n) = T(1) + c\left( \frac{n(n+1)}{2} - 1 \right) = O(n^2)\]O(n^2)\(T(n) = T(0) + T(n-1) + cn \quad \text{(or } T(n) = T(n-1) + cn\text{)}\)
-