분할 정복 (Devide and Conquer) 기법과 재귀 알고리즘을 이용한 정렬 알고리즘
주어진 배열을 원소가 하나 밖에 남지 않을 때까지 계속 둘로 나눈 후에 다시 크기 순으로 재배열하여 합침
전반적인 반복의 수는 점점 절반으로 줄어들기 때문에 O(logN) 시간이 필요하며, 병합시 모든 값들을 비교해야 하므로 O(N) 시간이 소모되어 총 시간 복잡도는 O(NlogN)
두 개의 배열을 병합할 때 병합 결과를 담아 놓을 배열이 추가로 필요하므로 공간 복잡도는 O(N)
재귀를 이용해 배열을 더 이상 나눌 수 없을 때까지 분할 후에, 병합하면서 점점 큰 배열을 만들어 나감
public class MergeSorter {
public static int[] sort(int[] arr) {
if (arr.length < 2) return arr;
int mid = arr.length / 2;
int[] low_arr = sort(Arrays.copyOfRange(arr, 0, mid));
int[] high_arr = sort(Arrays.copyOfRange(arr, mid, arr.length));
int[] mergedArr = new int[arr.length];
int m = 0, l = 0, h = 0;
while (l < low_arr.length && h < high_arr.length) {
if (low_arr[l] < high_arr[h])
mergedArr[m++] = low_arr[l++];
else
mergedArr[m++] = high_arr[h++];
}
while (l < low_arr.length) {
mergedArr[m++] = low_arr[l++];
}
while (h < high_arr.length) {
mergedArr[m++] = high_arr[h++];
}
return mergedArr;
}
}
병합 결과를 담을 새로운 배열을 매번 생성해서 리턴하지 않고, 인덱스 접근을 이용해 입력 배열을 계속해서 업데이트하면 메모리 사용량을 대폭 줄일 수 있음 (In-place sort)
public class MergeSorter {
public static void mergeSort(int[] arr) {
sort(arr, 0, arr.length);
}
private static void sort(int[] arr, int low, int high) {
if (high - low < 2) {
return;
}
int mid = (low + high) / 2;
sort(arr, 0, mid);
sort(arr, mid, high);
merge(arr, low, mid, high);
}
private static void merge(int[] arr, int low, int mid, int high) {
int[] temp = new int[high - low];
int t = 0, l = low, h = mid;
while (l < mid && h < high) {
if (arr[l] < arr[h]) {
temp[t++] = arr[l++];
} else {
temp[t++] = arr[h++];
}
}
while (l < mid) {
temp[t++] = arr[l++];
}
while (h < high) {
temp[t++] = arr[h++];
}
for (int i = low; i < high; i++) {
arr[i] = temp[i - low];
}
}
}