Study/Algorithm
분할 정복 (Divide and Conquer)
찬 주
2023. 8. 17. 16:15
분할 정복
- 그대로 해결할 수 없는 문제를 작은 문제로 분할하여 해결하는 방법
- 대표적인 예: Quick Sort, Merge Sort
- 재귀함수를 사용해 구현
분할 정복의 단점
- 재귀함수를 사용하기 때문에 오버헤드가 발생하며, 스택에 다양한 데이터를 보관하고 있어야 하므로 스택 오버플로우가 발생하거나 과도한 메모리 사용을 하게된다.
Merge Sort
정렬해야 할 리스트가 주어지면 해당 리스트를 최대한 작게 쪼갠 후, 인접한 원소들끼리 비교하며 정렬해 합치는 방법이다. 위 gif는 merge sort 과정에서 업데이트 되는 정렬 리스트를 보여주는 것이기 때문에 정렬 과정을 이해하기엔 어려우니 아래 그림을 통해 이해해보자. 이해가 쉽도록 리스트 원소의 개수를 8개로 하였다.
더이상 쪼갤 수 없을 때까지 쪼갠 후, 합치면서 정렬하는 것을 확인할 수 있다. 항상 2개로 나눠야하는 것은 아니지만, 보통 2개로 나누어 사용한다. 이러한 방식을 two-way 방식이라고 한다.
여기서 노란색으로 칠해진 리스트들은 모두 정렬된 상태인 것을 알 수 있다. 즉, 병합할 때 정렬된 상태로 합쳐야된다는 것이다. 이때 다른 정렬 방법을 사용하는 것이 아니라, 하나의 빈 리스트를 가지고 정렬 순서대로 넣어준 뒤 마지막에 원본 배열에 복사하면 된다.
코드
public class Main {
static int[] list;
static int[] sorted;
public static void main(String[] args) {
list = new int[]{46, 43, 21, 19, 30, 43, 20, 17, 35, 6};
sorted = new int[list.length];
mergeSort(0, list.length - 1);
for (int i = 0; i < list.length; i++) {
System.out.print(list[i] + " ");
}
System.out.println();
}
private static void mergeSort(int left, int right) {
if (left == right) {
return;
}
int mid = (left + right) / 2;
mergeSort(left, mid);
mergeSort(mid + 1, right);
merge(left, mid, right);
}
private static void merge(int left, int mid, int right) {
int l = left;
int r = mid + 1;
int idx = left;
while (l <= mid && r <= right) {
if (list[l] < list[r]) {
sorted[idx++] = list[l++];
} else {
sorted[idx++] = list[r++];
}
}
while (l <= mid) {
sorted[idx++] = list[l++];
}
while (r <= right) {
sorted[idx++] = list[r++];
}
for (int i = left; i <= right; i++) {
list[i] = sorted[i];
}
}
}