합병정렬(merge sort)는 평균 시간복잡도 O(nlogn)의 비교 기반 정렬 알고리즘입니다. 대표적인 divide and conquer (분할정복) 알고리즘을 활용하여 구현하는 정렬 알고리즘 중 하나입니다. (Safari, Firefox의 Array.prototype.sort가 merge sort 알고리즘으로 구현되어 있습니다.) 좋은 성능을 가지고 있고, 구현하기 쉽고 이해하기도 쉬운 장점을 가진 정렬 알고리즘입니다.
합병정렬은 어떻게 동작할까요? 합병정렬은 하나의 정렬되지 않은 배열을 정렬하는 것보다 두 개의 정렬된 배열을 정렬하는 것이 더 효율적이다라는 아이디어로 시작합니다. 두 개의 정렬된 배열이 있다면, 하나씩 요소를 비교하고, 작은 값을 결과 배열에 하나씩 추가하기 시작합니다. A와 B의 배열이 있다고 생각해 봅시다. A[0]와 B[0]의 요소를 비교합니다. A[0] 요소가 더 작다면 A[0] 요소를 결과 배열에 넣습니다. A[1]과 B[0]를 비교합니다. B[0]가 더 작다면 B[0]를 결과 배열에 넣습니다. A[1]과 B[1]을 비교합니다. 이렇게 비교가 끝나면 결과 배열은 A와 B 배열의 비교 값에 대해서 정렬된 상태가 될 것입니다. (A배열과 B배열은 정렬된 상태라 추가적으로 정렬할 필요가 없다)
구현 방법은 크게 3가지로 구성되어 있습니다.
[7, 1, 5, 2]
[7, 1] | [5, 2]
[7] | [1] [5] | [2]
[1, 7] | [2, 5]
[1, 2, 5, 7]
// 배열을 반으로 나누고, 재귀적으로 병합한다.
function mergeSort(arr) {
// 재귀함수 탈출조건 단일 요소로 구성된 배열이라면, 리턴
if (arr.length === 1) {
return arr;
}
const middle = Math.floor(arr.length / 2); // 배열의 중간 값
const left = arr.slice(0, middle); // left side items
const right = arr.slice(middle); // right side items
return merge(mergeSort(left), mergeSort(right));
}
// 배열을 비교하고, 연결된 결과를 리턴한다.
function merge(left, right) {
let result = [];
let leftIndex = 0;
let rightIndex = 0;
// left items와 right items 비교
while(leftIndex < left.length && rightIndex < right.length) {
if (left[leftIndex] < right[rightIndex]) {
result.push(left[leftIndex]);
leftIndex++;
} else {
result.push(right[rightIndex]);
rightIndex++;
}
}
return [...result, ...left.slice(leftIndex), ...right.slice(rightIndex)];
}
const list = [2, 5, 1, 3, 7, 2, 3, 8, 6, 3];
console.log(mergeSort(list)); // [ 1, 2, 2, 3, 3, 3, 5, 6, 7, 8 ]