배열을 앞부분과 뒷부분으로 나누어 각각 정렬한 다음 합병하는 작업을 반복하여 정렬
합병정렬은 안전 정렬에 속하며, 분할 정복 알고리즘의 한 종류이다.
합병 정렬 알고리즘의 순서는 다음과 같다.
1, 3
으로 병합2, 6
으로 병합
합병정렬 전체 과정 애니메이션이다.
public class MergeSortTest {
static int[] buff; // 임시 배열은 전역 변수로 지정하여 매번 buff를 새로 생성하지 않도록 한다.
public static void mergeSort(int[] arr) {
// 배열 크기만큼 임시 배열 생성
buff = new int[arr.length];
// 분할 정복을 이용한 배열 전체 합병정렬
mergeSort(arr, 0, arr.length - 1);
}
private static void mergeSort(int[] arr, int left, int right) {
// 크키가 1보다 큰 경우만 분할
if (left < right) {
int center = left + (right - left) / 2; // 중간을 기점으로 균등 분할 (분할)
mergeSort(arr, left, center); // 배열의 앞부분을 재귀적으로 합병정렬 (정복)
mergeSort(arr, center + 1, right); // 배열의 뒷부분을 재귀적으로 합병정렬 (정복)
merge(arr, left, center, right); // 실제 합병 수행
}
}
private static void merge(int[] arr, int left, int center, int right) {
// i는 arr[left] ~ arr[right] 배열의 인덱스
int i;
// p는 buff에 복사한 요소의 개수
// j는 buff의 값과 arr의 값 비교 시 buff에서 사용하는 인덱스
int p = 0, j = 0;
// 새로 합병될 arr의 인덱스
int k = left;
// 배열의 앞부분 (a[left] ~ a[center])을 buff[0] ~ buff[center - left]에 복사.
for (i = left; i <= center; i++)
buff[p++] = arr[i];
// 배열의 뒷부분 (a[center + 1] ~ a[right])과 buff로 복사한 배열의 앞부분 p개를 병합하여 배열 arr에 저장
while (i <= right && j < p)
arr[k++] = (buff[j] <= arr[i]) ? buff[j++] : arr[i++];
// 배열 buff에 남아 있는 요소를 배열 arr에 복사
while (j < p)
arr[k++] = buff[j++];
}
public static void main(String[] args) {
int[] arr = {3, 1, 2, 6, 7 , 5, 4};
// 합병정렬
mergeSort(arr);
// 결과 출력
System.out.println(Arrays.toString(arr));
}
}
코드의 merge()
메소드 흐름을 그림으로 조금 더 자세히 살펴보자.
arr[0]
을 buff 임시 배열에 복사한다.
arr[1]
과 buff로 복사한 앞부분 arr[0]
을 병합하여 arr 배열에 저장
arr[0] ~ arr[1]
을 buff 임시 배열에 복사한다.
arr[2] ~ arr[3]
과 buff로 복사한 앞부분 arr[0] ~ arr[1]
을 병합하여 arr 배열에 저장 arr[0]
에 들어간다.
arr[1]
에 들어간다.
arr[2]
에 들어간다.
장점
n번 호출
logN 만큼의 시간 소요
O(nlogn)
의 시간복잡도를 가진다. 단점