Merge Sort

merge sort는 O(nlogn) 의 정렬 알고리즘이다. 분산 시스템이나 배열이 너무 커서 모든 배열을 메모리에 올려서 작업을 할 수 없을 때, 위의 정렬 방식을 사용한다. 배열을 모두 메모리에 올릴 수 있다면 Quick Sort가 더 효율적인 정렬 방식이다.

merge sort는 배열을 재귀적으로 다 분할해서, 합치면서 정렬을 한다. 즉

int [] arr = {4,5,9,3,2};

와 같은 배열이 있다면, 배열 전체를 재귀적으로 분할한다. 실제로는 이렇게 되지는 않지만 의미상으로는

int [] arr1 = {4};
int [] arr2 = {5};
int [] arr3 = {9};
int [] arr4 = {3};
int [] arr5 = {2};

이렇게 분할한다. 배열의 원소가 한 개가 되면 자동적으로 정렬된 상태가 되기 때문이다. 그다음에 배열을 합치면서(merge) 배열을 정렬한다. 즉, merge sort의 가장 핵심적인 문제는 이것이다.

두 개의 정렬된 배열을 어떻게 하면 합칠 수 있지?

코드는 다음과 같다.

Code

	public void mergeSort(int [] arr, int start, int end) {
		int [] arr2 = new int[arr.length];
 
		if(start < end) {
			int mid = (start + end) / 2;
			mergeSort(arr, start, mid);
			mergeSort(arr, mid+1, end);
			merge(arr, arr2, start, mid, end);
		}
	}
 
	private void merge(int[] arr, int[] arr2, int start, int mid, int end) {
		arrayCopy(arr, arr2, start, end);
		int startIdx = start;
		int midIdx = mid + 1;
 
		for(int i = start; i<=end; i++) {
			if(startIdx > mid) {
				arr[i] = arr2[midIdx++];
			} else if (midIdx > end) {
				arr[i] = arr2[startIdx++];
			} else if (arr2[startIdx] > arr2[midIdx]) {
				arr[i] = arr2[midIdx++];
			} else {
				arr[i] = arr2[startIdx++];
			}
		}
	}
 
	private void arrayCopy(int[] arr, int[] arr2, int start, int end) {
		for(int i = start; i <=end; i++) {
			arr2[i] = arr[i];
		}
	}

설명

mergeSort 메소드에서 재귀적으로 배열을 분할한다. 그렇게 되면, 두 번째 mergeSort 함수가 호출된 다음에 [start, mid], [mid+1, end]의 두 배열은 각각 정렬된 상태가 된다.

그러면 그 두 배열을 merge 메소드에서 합치게 된다. 배열을 합치기 전에, 원래 배열의 값을 값을 할당한 다른 배열에 복사한 다음에, 그 배열을 참조하여 두 개의 배열을 합치게 된다.

시간 복잡도 / 공간 복잡도

재귀트리를 그려보면 시간 복잡도가 O(nlogn)임을 알 수 있다. 다만 공간 복잡도 측면에서 봤을 때는, merge하는 전체 배열의 크기만큼의 공간이 더 필요함을 알 수 있다.