본문 바로가기
알고리즘

분할 정복(Divide and Conquer) 알고리즘 총 정리 - 개념, 원리, 동작예시, 장단점, 시간복잡도, C언어, Java, Python 예시코드

by Best Coding 2025. 2. 7.
반응형

분할 정복 알고리즘

 

1. 분할 정복(Divide and Conquer) 알고리즘이란?

분할 정복(Divide and Conquer) 알고리즘은 문제를 더 작은 하위 문제로 나누고(Divide), 각각을 독립적으로 해결한 후(Conquer), 그 결과를 합쳐서(Merge) 원래 문제의 해답을 도출하는 방식의 알고리즘입니다. 이 알고리즘은 재귀(Recursion)를 활용하여 문제를 단계적으로 해결하는 것이 특징입니다.

반응형

 

 

 

2. 분할 정복 알고리즘의 동작 원리

분할 정복 알고리즘은 다음과 같은 세 가지 단계로 이루어집니다.

  1. 분할(Divide): 문제를 동일한 유형의 작은 하위 문제로 나눕니다.
  2. 정복(Conquer): 나눈 하위 문제를 개별적으로 해결합니다. 보통 이 단계에서 재귀적으로 호출됩니다.
  3. 결합(Merge): 해결된 하위 문제들을 합쳐서 최종적인 결과를 도출합니다.

 

동작 원리 예제: 병합 정렬(Merge Sort)

병합 정렬을 통해 분할 정복 알고리즘의 원리를 쉽게 이해할 수 있습니다.

 

예제 배열: [38, 27, 43, 3, 9, 82, 10]

1️⃣ 분할 단계(Divide)

  • 주어진 배열을 반으로 나눕니다.
[38, 27, 43, 3]  |  [9, 82, 10]
  • 각 부분을 다시 반으로 나눕니다.
[38, 27]  |  [43, 3]  |  [9, 82]  |  [10]
  • 배열의 크기가 1이 될 때까지 계속 나누어 줍니다.
[38]  |  [27]  |  [43]  |  [3]  |  [9]  |  [82]  |  [10]

 

2️⃣ 정복 단계(Conquer)

  • 크기가 1인 배열은 이미 정렬된 상태이므로, 두 개씩 묶어 정렬을 수행합니다.
[27, 38]  |  [3, 43]  |  [9, 82]  |  [10]
  • 다시 정렬하여 더 큰 배열을 만듭니다.
[3, 27, 38, 43]  |  [9, 10, 82]

 

3️⃣ 결합 단계(Merge)

  • 두 개의 정렬된 배열을 합쳐서 최종적으로 정렬된 배열을 만듭니다.
[3, 9, 10, 27, 38, 43, 82]

이제 원래 배열이 정렬되었습니다!

 

 

 

3. 분할 정복 알고리즘의 시간 복잡도

  • 대부분의 분할 정복 알고리즘의 시간 복잡도는 O(n log n)입니다.
  • 예를 들어, 병합 정렬(Merge Sort)의 경우:
    • 분할(Divide) 단계: 배열을 계속 절반으로 나누므로 O(log n)
    • 정복(Conquer) 및 결합(Merge) 단계: 각 단계에서 n개의 요소를 정렬하므로 O(n)
    • 최종적으로 O(n log n) 복잡도를 가짐.

 

 

4. 예제 코드 (Merge Sort)

C 언어 구현

#include <stdio.h>

void merge(int arr[], int left, int mid, int right) {
    int i, j, k;
    int n1 = mid - left + 1;
    int n2 = right - mid;
    int L[n1], R[n2];
    
    for (i = 0; i < n1; i++)
        L[i] = arr[left + i];
    for (j = 0; j < n2; j++)
        R[j] = arr[mid + 1 + j];
    
    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);
    }
}

 

Java 구현

public class MergeSort {
    void merge(int arr[], int left, int mid, int right) {
        int n1 = mid - left + 1;
        int n2 = right - mid;
        int L[] = new int[n1];
        int R[] = new int[n2];
        
        for (int i = 0; i < n1; i++)
            L[i] = arr[left + i];
        for (int j = 0; j < n2; j++)
            R[j] = arr[mid + 1 + j];
        
        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);
        }
    }
}

 

Python 구현

def merge(arr, left, mid, right):
    L = arr[left:mid+1]
    R = arr[mid+1:right+1]
    i = j = 0
    k = left
    while i < len(L) and j < len(R):
        if L[i] <= R[j]:
            arr[k] = L[i]
            i += 1
        else:
            arr[k] = R[j]
            j += 1
        k += 1
    
    while i < len(L):
        arr[k] = L[i]
        i += 1
        k += 1
    while j < len(R):
        arr[k] = R[j]
        j += 1
        k += 1

def merge_sort(arr, left, right):
    if left < right:
        mid = (left + right) // 2
        merge_sort(arr, left, mid)
        merge_sort(arr, mid + 1, right)
        merge(arr, left, mid, right)

 

 

 

 

5. 분할 정복 알고리즘의 주요 사용처

1️⃣ 정렬 문제 (Sorting)

  • 병합 정렬(Merge Sort): 데이터를 정렬할 때 사용되는 대표적인 분할 정복 알고리즘입니다.
  • 퀵 정렬(Quick Sort): 피벗을 기준으로 배열을 나누고 정렬하는 방식입니다.
  • 병합 정렬과 퀵 정렬 관련 내용은 아래 포스팅에 자세히 설명했습니다.

2024.12.31 - [알고리즘] - 병합 정렬 (Merge Sort) 총 정리 - 개념, 원리, 동작 예시, 시간복잡도, C언어, Java, Python 예시코드, 주의점, 장단점

 

병합 정렬 (Merge Sort) 총 정리 - 개념, 원리, 동작 예시, 시간복잡도, C언어, Java, Python 예시코드, 주

1. 병합 정렬이란?병합 정렬은 "분할 정복(Divide and Conquer)" 기법을 이용한 정렬 알고리즘으로, 리스트를 반으로 나누고 각각을 재귀적으로 정렬한 후 병합하여 정렬된 리스트를 만드는 방식입니

best-coding.tistory.com

2024.12.31 - [알고리즘] - 퀵 정렬 (Quick Sort) 총 정리 - 개념, 원리, 동작 예시, 시간 복잡도, C언어, Java, Python 예시코드, 주의점, 장단점

 

퀵 정렬 (Quick Sort) 총 정리 - 개념, 원리, 동작 예시, 시간 복잡도, C언어, Java, Python 예시코드, 주의

1. 퀵 정렬이란?퀵 정렬은 "분할 정복(Divide and Conquer)" 기법을 활용한 정렬 알고리즘입니다. 기준이 되는 피벗(Pivot)을 설정하고, 이를 기준으로 작은 값과 큰 값을 분리한 뒤 재귀적으로 정렬합니

best-coding.tistory.com

 

 

2️⃣ 탐색 문제 (Searching)

  • 이진 탐색(Binary Search): 정렬된 배열에서 특정 값을 찾을 때, 탐색 범위를 절반으로 줄여가는 방식입니다.
  • 이진 탐색 알고리즘은 아래 링크에 자세히 정리했습니다.

2024.12.19 - [알고리즘] - 이진 탐색(Binary Search) 알고리즘 - C언어, Java, Python 예시코드, UpperBound, LowerBound

 

이진 탐색(Binary Search) 알고리즘 - C언어, Java, Python 예시코드, UpperBound, LowerBound

이진 탐색(Binary Search)이진 탐색(Binary Search)은 정렬된 배열에서 원하는 값을 빠르게 찾아내는 효율적인 알고리즘입니다. 이진 탐색은 중간값을 반복적으로 비교하며 탐색 범위를 반으로 줄여 나

best-coding.tistory.com

 

 

3️⃣ 행렬 연산(Matrix Multiplication)

  • 스트라센 알고리즘(Strassen’s Algorithm): 행렬 곱셈을 더 빠르게 수행하기 위한 알고리즘입니다.

4️⃣ 그래프 알고리즘

  • 최소 비용 신장 트리(Minimum Spanning Tree) - 크루스칼 알고리즘: 분할 정복을 활용하여 최소 비용 신장 트리를 찾습니다.

5️⃣ 최근접 쌍 문제 (Closest Pair of Points)

  • 평면상의 점들 중에서 가장 가까운 두 점을 찾는 문제에서 분할 정복을 활용할 수 있습니다.

 

 

6. 분할 정복 알고리즘의 장단점

✅ 장점

  • 효율적인 시간 복잡도: O(N log N) 혹은 그 이상의 성능을 보이는 경우가 많음.
  • 재귀를 활용한 직관적인 문제 해결: 문제를 단계별로 나누어 해결할 수 있음.
  • 병렬 처리 가능성: 독립적인 하위 문제들을 동시에 처리할 수 있어 병렬 처리에 적합함.

 

❌ 단점

  • 추가적인 메모리 사용: 병합 정렬과 같은 경우 추가적인 공간이 필요할 수 있음.
  • 재귀 호출에 따른 오버헤드: 함수 호출이 많아지면 오버헤드가 증가할 수 있음.
  • 적용 가능한 문제의 제한: 모든 문제에 적용할 수 있는 것은 아니며, 특정 문제 유형에 적합함.

 

분할 정복 알고리즘은 문제를 작은 단위로 나누고, 해결한 후 이를 병합하는 방식으로 다양한 문제에 활용됩니다. 정렬, 탐색, 그래프 문제뿐만 아니라 다양한 최적화 문제에서도 효율적으로 사용되므로, 이를 이해하고 적절하게 활용하는 것이 중요합니다.

반응형

댓글