[알고리즘] 분할 정복(Divide and Conquer)
✅ 분할 정복이란?
분할 정복(Divide and Conquer)은 큰 문제를 더 작고 동일한 형태의 문제로 분할한 뒤, 각각을 재귀적으로 해결하고 그 결과를 결합(merge)하여 전체 문제를 푸는 알고리즘 설계 기법
🔧 핵심 구조
Divide : 문제를 더 작은 부분 문제로 나눈다.
Conquer : 각 부분 문제를 재귀적으로 해결한다.
Combine : 해결된 결과를 결합하여 최종 답을 얻는다.
🎯 분할 정복을 사용해야 하는 문제의 특징
특징 | 설명 |
---|---|
문제를 쪼갤 수 있다 | 문제를 더 작고 동일한 구조로 나눌 수 있어야 한다 |
부분 문제의 결과로 원래 문제를 해결할 수 있다 | 각 부분 문제의 결과를 합쳐 전체 결과를 만들 수 있어야 한다 |
부분 문제들이 독립적이다 | 각각의 하위 문제는 다른 문제에 영향을 주지 않고 개별적으로 풀 수 있어야 한다 |
중복 계산이 거의 없다 | 동일한 부분 문제를 반복 계산하지 않아도 되는 구조 (DP와 차이점) |
전체 문제를 직접 풀기보다 나눠서 푸는 것이 더 효율적이다 | 시간 복잡도 개선 가능성 있음 |
📌 수도코드(pseudocode) 구조
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
함수 divideAndConquer(input):
// 1. 기저 조건(base case) => 재귀 종료 조건
if input is small or simple enough:
return solve(input) // 재귀 없이 직접 처리할 수 있는 가장 작은 단위 문제에 대한 해결 함수
// 2. 분할 (Divide)
subInputs = divide(input) // 문제를 더 작은 동일한 형태의 부분 문제로 나누는 함수
// 3. 정복 (Conquer) => 문제의 핵심 계산이 일어남
subResults = []
for sub in subInputs:
result = divideAndConquer(sub) // 분할된 문제에 대해 재귀적으로 같은 방식으로 해결
subResults.append(result)
// 4. 결합 (Combine) => 부분 문제들의 결과를 모아서 전체 문제의 결과로 합침
return combine(subResults)
✅ 분할 정복 기법을 사용하는 알고리즘
알고리즘 | 설명 |
---|---|
병합 정렬 (Merge Sort) | 배열을 반씩 나누고 정렬한 후 병합 |
퀵 정렬 (Quick Sort) | 피벗을 기준으로 분할하며 정렬 |
이진 탐색 (Binary Search) | 정렬된 배열을 반씩 나누며 탐색 |
거듭제곱 계산 (Exponentiation by Squaring) | 수나 행렬을 빠르게 제곱 |
최대 구간 합 (Divide and Conquer 방식의 Kadane 변형) | 배열을 좌우로 나누어 최대 합 찾기 |
가장 가까운 두 점 문제 (Closest Pair of Points) | 2D 평면에서 유클리드 거리 최소 쌍 찾기 |
⚠️ 주의할 점: 함수 오버헤드
분할 정복은 보통 재귀 호출을 사용
함수 호출이 많아지면 함수 오버헤드가 성능에 영향을 줄 수 있음
다만 대부분의 경우 오버헤드는 무시 가능하며, 코드 가독성과 구조화 측면에서 함수 분리는 유익함
-
오버헤드를 고려해야 하는 상황
재귀 호출이 10⁷~10⁹번 이상인 경우
-
자바에서 스택 깊이 초과 (StackOverflow) 위험이 있는 경우
- 일반적인 JVM에서는 약 7,000~10,000 재귀 깊이에서 StackOverflowError 발생
🧩 연습문제
문제 번호 | 문제 이름 | 설명 | 풀이 포스팅 |
---|---|---|---|
BOJ 2630 | 색종이 만들기 | 분할 정복으로 영역 색상 확인 | 풀이 보기 🔗 |
BOJ 17829 | 222-풀링 | 2차원 배열을 반복적으로 압축하는 분할 정복 문제 | 풀이 보기 🔗 |
BOJ 1992 | 쿼드트리 | 2차원 분할 정복 구조 익히기 | 풀이 보기 🔗 |
BOJ 2447 | 별 찍기 - 10 | 분할 정복 기반의 재귀 패턴 출력 | - |