합병 알고리즘은 중급 알고리즘이라고 할 수 있습니다. 기초 알고리즘(선택 삽입 버블) 보다는 빠르지만 코드가 직관적이지는 않습니다. 코드를 작성해야한다는 부담감보다는 알고리즘의 동작 원리를 이해하는 것으로 목표합시다. (추후에는 코드로...짜보는 것 까지 해야겠죠 ㅎㅎ)
Don't be scared!
주제
위의 그림에서 빨간선은 분할하는 모습이며 파란선이 병합하는 모습입니다.
의사코드
말로는 참 어렵습니다... 예시를 들어보겠습니다.
a배열 : [1,10,50] 과 b배열 : [2,14,99,100] 두개의 "정렬된" 배열이 있습니다.
1. 빈배열을 만듭니다. [] 이를 결과 배열이라 하겠습니다.
2. a배열 값 1 과 b배열 값 2를 비교합니다. 그리고 작은 수 1를 결과 배열에 넣습니다 (결과배열 [1]
3. a배열 값 10 과 b배열 값 2를 비교, 결과 배열에 2 추가 -> 결과 배열 [1,2]
4. a배열 값 10 과 b배열 값 14를 비교, 결과 배열에 10 추가 -> 결과 배열 [1,2,10]
이런식으로 비교를 진행하게 되는 방식입니다.
// Merges two already sorted arrays
function merge(arr1, arr2){
let results = [];
let i = 0;
let j = 0;
while(i < arr1.length && j < arr2.length){
if(arr2[j] > arr1[i]){
results.push(arr1[i]);
i++;
} else {
results.push(arr2[j])
j++;
}
}
while(i < arr1.length) {
results.push(arr1[i])
i++;
}
while(j < arr2.length) {
results.push(arr2[j])
j++;
}
return results;
}
merge([100,200], [1,2,3,5,6])
// Merge function from earlier
function merge(arr1, arr2){
let results = [];
let i = 0;
let j = 0;
while(i < arr1.length && j < arr2.length){
if(arr2[j] > arr1[i]){
results.push(arr1[i]);
i++;
} else {
results.push(arr2[j])
j++;
}
}
while(i < arr1.length) {
results.push(arr1[i])
i++;
}
while(j < arr2.length) {
results.push(arr2[j])
j++;
}
return results;
}
// Recrusive Merge Sort
function mergeSort(arr){
if(arr.length <= 1) return arr;
let mid = Math.floor(arr.length/2);
let left = mergeSort(arr.slice(0,mid));
let right = mergeSort(arr.slice(mid));
return merge(left, sright);
}
mergeSort([10,24,76,73])
시간 복잡도가 왜? n * log n 일까요
O(log N)은 N 증가에 따른 분할 수 입니다.
(예를들어 값 8개를 갖는 배열의 경우 3번의 분할이 필요합니다)
(예를들어 값 16개를 갖는 배열의 경우 4번의 분할이 필요합니다)
매번 분할 할때마다 합병을 위해서는 O(n)이 필요합니다.
([3,8][4,6] 를 합병하려면 3번을 비교해야하죠)
([1,3,8][2,4,6] 를 합병하려면 5번을 비교해야하죠)
이미 정렬된 배열을 입력 받기 때문에 가능한 시간복잡도 입니다.
두개가 모두 정상 작동해야하기 때문에 전체 시간 복잡도는 N*log n 이 형성 됩니다.