병합 정렬은 분할 정복 알고리즘이다.
분할 정복 알고리즘이란 무엇일까.
분할 정복 알고리즘은 크게 분할, 정복, 결합 과정으로 나누어진다.
분할
문제를 해결하기 위해서 더 작은 규모의 문제로 쪼갬
정복
작게 나누어진 문제들을 개별적으로 해결
결합
해결된 개별의 문제들을 다시 합쳐서 원래의 문제를 해결하는 과정
38, 27, 43, 3, 9, 82, 10, 12]
길이 1이 될 때까지 다 나누면[38], [27], [43], [3], [9], [82], [10], [12]
의 모습이 된다.[38], [27], [43], [3], [9], [82], [10], [12]
[27, 38], [3, 43], [9, 82], [10, 12]
[3, 27, 38, 43], [9, 10, 12, 82]
[3, 9, 10, 12, 27, 38, 43, 82]
private void MergeSort(List<Ranking> list)
{
if (list.Count <= 1)
return;
int mid = list.Count / 2;
List<Ranking> left = new List<Ranking>(list.GetRange(0, mid));
List<Ranking> right = new List<Ranking>(list.GetRange(mid, list.Count - mid));
MergeSort(left);
MergeSort(right);
Merge(list, left, right);
}
Merge
메서드의 매개변수로 주어 병합한다.private void Merge(List<Ranking> list, List<Ranking> left, List<Ranking> right)
{
int i = 0, j = 0, k = 0;
while (i < left.Count && j < right.Count)
{
if (left[i].Score >= right[j].Score)
{
list[k++] = left[i++];
}
else
{
list[k++] = right[j++];
}
}
while (i < left.Count)
{
list[k++] = left[i++];
}
while (j < right.Count)
{
list[k++] = right[j++];
}
}
list
)와, 작게 나뉘어진 두 개의 리스트(left
, right
)를 매개변수로 받는다.left
와 right
의 맨 앞 요소를 비교해서 더 작거나 같은 요소를 list
에 추가해준다.list
에 작은 순서대로 요소가 쌓인다.left
혹은 right
에 요소가 남아있다면 순서대로 list
에 추가해준다.병합 정렬의 시간 복잡도는 항상 O(n log n)이다.
분할 단계가 (log n) 번 진행되고, 각 단계에서 병합 작업이 리스트의 길이에 비례하여 (O(n)) 시간이 소요된다.
두 요소를 곱하면 O(n log n)가 나온다.
그리고 모든 케이스에서 동일한 과정을 거치기 때문에 빠르거나 느려도 O(n log n)의 시간 복잡도를 가진다.
예측 가능한 성능
최선, 평균, 최악에서 모두 O(n log n)의 성능을 내어 성능 예측이 가능하다.
안정성
동일한 요소를 정렬 시 순서가 유지된다.
두 개의 연속 데이터에서 하나씩 비교하여 정렬할 때, 비교하는 요소가 동일한 값인 경우에 원래의 순서를 유지한 채로 정렬이 된다.
추가 메모리 사용
배열이나 리스트를 나눌 때 새로운 공간이 필요하기 때문에 추가로 메모리가 더 필요하다.
메모리가 제한적인 상황에서는 사용 고려할 필요 있음.
재귀 호출 오버헤드
재귀 호출로 인한 오버헤드가 배열, 리스트의 길이와 비례하여 자주 발생한다.
비교적 높은 상수 계수
배열을 병합할 때 데이터 복사가 자주 일어나며, 배열들이 인접해있지 않을 수 있기 때문에 메모리 캐시 효율성이 떨어진다.
이런 특징들과 위에 적은 단점들 때문에 상수 계수가 높아질 수 있다.
병합 정렬은 이론 상 이상적인 정렬 알고리즘이지만, 실제로는 상수 계수가 높을 수 있다는 예측 불가능한 단점이 있다.
이를 해결하기 위한 최적화 방법이 있다. (우선 2개 정도만)
삽입 정렬과의 하이브리드
배열의 나눠지는 최소 길이를 정해놓고, 그 기준에 맞게 제일 작게 나눠진 배열은 삽입 정렬을 사용하는 것이다.
이렇게 하면 단점에 적어놓은 상수 계수가 높아지는 것을 해소할 수 있다.
상향식 병합 정렬
재귀 호출을 사용하지 않는 병합 정렬인데, 아직 이해도가 낮아서 따로 포스팅할 예정이다.