🔖 INDEX
병합 정렬(Merge Sort)은 분할 정복(Divide and Conquer) 방식을 사용하는 비교 기반 정렬 알고리즘입니다. 병합 정렬은 배열을 두 개의 동일한 크기의 하위 배열로 분할한 다음, 하위 배열을 정렬한 후 다시 병합하는 과정을 통해 정렬을 수행합니다. 병합 정렬은 안정적이며, 빠르게 작동하므로 큰 데이터 셋에 적합한 알고리즘입니다.
병합 정렬의 작동 원리
병합 정렬의 작동 원리는 다음과 같습니다:
- 배열의 크기가 1 또는 0이 될 때까지 배열을 절반으로 나누어 재귀적으로 분할합니다.
- 분할된 하위 배열을 정렬하고 병합하는 과정을 시작합니다. 이 때 하위 배열의 크기가 1이면 이미 정렬된 것으로 간주합니다.
- 두 개의 인접한 정렬된 하위 배열을 병합하여 새로운 정렬된 배열을 만듭니다. 병합 과정에서 두 하위 배열의 원소들을 비교하며 작은 값을 새 배열에 삽입하고, 해당 원소를 원래 하위 배열에서 삭제합니다. 이 과정을 두 하위 배열의 모든 원소가 새 배열에 삽입될 때까지 반복합니다.
- 모든 하위 배열이 병합되어 원래 배열의 크기와 동일한 크기의 정렬된 배열이 완성될 때까지 이 과정을 반복합니다.
병합 정렬의 특징
병합 정렬의 특징은 다음과 같습니다:
- 안정적인 정렬(Stable Sort): 같은 값의 원소들은 정렬 과정에서 상대적인 순서가 변경되지 않습니다.
- 비제자리 정렬(Out-of-place Sort): 정렬 과정에서 추가적인 메모리를 사용합니다. 이는 병합 과정에서 두 하위 배열을 새로운 배열에 합치는 과정에서 발생합니다.
- 시간 복잡도(Time Complexity): 최선, 평균, 최악의 경우 모두 O(n log n)의 시간 복잡도를 가집니다. 이로 인해 큰 데이터 셋에 적합한 알고리즘입니다.
- 재귀적(Recursive): 병합 정렬은 재귀적으로 배열을 분할하고 병합하는 과정을 수행합니다.
병합 정렬은 큰 데이터 셋에서 빠른 성능을 보이지만, 추가적인 메모리 사용량이 비교적 높습니다. 이 때문에 메모리 제약이 있는 상황에서는 다른 알고리즘을 사용하는 것이 좋습니다. 예를 들어, 퀵 정렬과 힙 정렬은 추가 메모리 사용량이 적은 대안이 될 수 있습니다.
병합 정렬의 장점
- 안정적인 정렬: 같은 값의 원소들은 정렬 과정에서 상대적인 순서가 변경되지 않습니다.
- 큰 데이터 셋에 효율적: 병합 정렬은 O(n log n)의 시간 복잡도를 가지므로 큰 데이터 셋에서도 빠른 성능을 보입니다.
- 고르게 분할된 작업: 병합 정렬은 배열을 고르게 분할하여 작업을 수행하므로 최악의 경우에도 균일한 성능을 보입니다.
병합 정렬의 단점
- 추가 메모리 사용: 병합 정렬은 두 하위 배열을 새로운 배열에 합치는 과정에서 추가적인 메모리를 사용합니다. 이로 인해 메모리 제약이 있는 상황에서는 더 적합한 알고리즘을 선택하는 것이 좋습니다.
- 재귀적 구현: 병합 정렬은 재귀적으로 구현되어 있으므로, 스택 오버플로와 같은 문제가 발생할 수 있습니다. 이를 방지하기 위해 반복적인 구현 방법도 사용할 수 있습니다.
병합 정렬의 구현 예제
병합 정렬은 자바에서 다음과 같이 구현할 수 있습니다.
// MergeSort 클래스 선언
public class MergeSort {
// 병합 과정을 수행하는 함수
public static void merge(int[] arr, int left, int middle, int right) {
// 구간의 크기를 계산
int n1 = middle - left + 1;
int n2 = right - middle;
// 임시 배열 생성
int[] leftArr = new int[n1];
int[] rightArr = new int[n2];
// 원래 배열에서 임시 배열로 데이터 복사
System.arraycopy(arr, left, leftArr, 0, n1);
System.arraycopy(arr, middle + 1, rightArr, 0, n2);
// 인덱스 초기화
int i = 0, j = 0, k = left;
// 두 배열을 비교하면서 병합
while (i < n1 && j < n2) {
if (leftArr[i] <= rightArr[j]) {
arr[k] = leftArr[i++];
} else {
arr[k] = rightArr[j++];
}
k++;
}
// 왼쪽 배열에 남은 원소를 복사
while (i < n1) {
arr[k++] = leftArr[i++];
}
// 오른쪽 배열에 남은 원소를 복사
while (j < n2) {
arr[k++] = rightArr[j++];
}
}
// 병합 정렬 알고리즘을 수행하는 함수
public static void mergeSort(int[] arr, int left, int right) {
if (left < right) {
// 중간 인덱스 계산
int middle = (left + right) / 2;
// 왼쪽 부분 배열 정렬
mergeSort(arr, left, middle);
// 오른쪽 부분 배열 정렬
mergeSort(arr, middle + 1, right);
// 병합
merge(arr, left, middle, right);
}
}
// 메인 함수
public static void main(String[] args) {
// 정렬할 배열
int[] arr = {64, 34, 25, 12, 22, 11, 90};
// 정렬 전 배열 출력
System.out.println("Unsorted array:");
for (int value : arr) {
System.out.print(value + " ");
}
// 병합 정렬 실행
mergeSort(arr, 0, arr.length - 1);
// 정렬 후 배열 출력
System.out.println("\nSorted array:");
for (int value : arr) {
System.out.print(value + " ");
}
}
}
병합 정렬은 재귀적 구현이 일반적이기 때문에 재귀 호출에 대한 깊이 제한이 적용되어 있을 경우, 깊이 제한에 도달하기 전에 정렬이 완료되지 않을 수 있습니다. 이 경우 반복적인 구현 방법을 사용하거나, 깊이 제한을 적절히 조절해야 합니다.
또한, 메모리 사용량이 큰 편이므로 메모리 제약이 있는 시스템에서 사용하기에는 부적합할 수 있습니다. 이러한 상황에서는 퀵 정렬, 힙 정렬 등 메모리 사용량이 상대적으로 적은 정렬 알고리즘을 고려해 볼 수 있습니다.
'프로그래밍 > JAVA' 카테고리의 다른 글
초보 자바 프로그래밍(23) - 힙정렬 (Heap Sort) (0) | 2023.05.02 |
---|---|
초보 자바 프로그래밍(22) - 퀵정렬 (Quick Sort) (0) | 2023.05.02 |
초보 자바 프로그래밍(20) - 삽입정렬 (Insertion Sort) (0) | 2023.05.02 |
초보 자바 프로그래밍(19) - 선택정렬 (Selection Sort) (0) | 2023.05.02 |
초보 자바 프로그래밍(18) - 버블정렬 (Bubble Sort) (0) | 2023.05.02 |
댓글