Merge Sort - 합병정렬

Merge Sort - 합병정렬

개요

  • Merge Sort 의 divide, merge 부분 동작 파악
  • 구현 - Java
  • 시간복잡도 분석 - 점화식 풀이
  • 코드 보기


Merge Sort

  1. 배열의 길이가 0 또는 1이면 이미 정렬된 것으로 본다. 그렇지 않은 경우에는
  2. 정렬되지 않은 리스트를 절반으로 잘라 비슷한 크기의 두 부분 리스트로 나눈다.
  3. 각 부분 리스트를 재귀적으로 합병 정렬을 이용해 정렬한다.
  4. 두 부분 리스트를 다시 하나의 정렬된 리스트로 합병한다.


Divide

Divide 에 대한 부분은 코드에서 mergeSort() 라는 메소드가 실행한다. 재귀적으로 배열을 반씩 자르고, 길이가 0 또는 1 이 되었을 때 merge 하는 과정은 아래와 같다.


Merge

merge() 메소드가 동작하는 과정은 아래와 같다. 몇 번의 divide(), merge() 작업이 반복적으로 실행 됐고, 최종적으로 처음에 divide 했던 두개의 sub array 를 merge 하는 과정이다. 두개의 sub array 들은 각각 정렬이 되어 있는 상태다.

바로 위 슬라이드에서 마지막 merge 가 실행되는 과정을 하나씩 살펴 본 슬라이드이다.


구현

github 에서 보기


분석

Merge sort 는 O(nlogn) 의 시간복잡도를 가진다. 어떻게 그렇게 되는지 보자.

T(n) = 2T(n/2) + n

위 점화식은 merge sort 를 표현한 점화식이다. T(n/2) 는 divide 하는 과정이고 n 은 merge 에 대한 부분이다. T(n) 을 T(n/2) + n 으로 표현 가능하다면 T(n/2) 는 아래와 같이 표현 가능하다.

T(n/2) = 2T(n/4) + n/2

각각의 T(x) 를 재귀적으로 표현해보면

T(n/4) = 2T(n/8) + n/4

와 같이 표현 가능하다.


각각의 점화식을 트리구조로 표현해보면 아래와 같다.

recurrence-relation-tree
Recurrence Relation Tree

위 그림에서 T(n) 을 표현한 트리에서 T(n/2)을 T(n/2) 을 표현한 트리로 대치시키고, T(n/2) 을 표현한 트리에서 T(n/4) 을 표현한 트리로 계속해서 대치시켜 나가면 아래와 같은 트리를 그릴 수 있다.

recurrence-tree
Recurrence Tree

트리의 각 레벨에서 노드의 개수와, 각 레벨의 합을 구하면 아래와 같다.

recurrence-tree-level-sum
Recurrence Tree Level Sum

Level 0 에는 n이 1개 있고, level 1 에는 n/2 이 2개, level 2 에는 n/4 이 4개, … , level h 에는 T(1) 이 2h 개 있다. T(1) = 1 로 표현 가능하므로 h = log2n 이라는 결과를 얻을 수 있다.

2h * T(1) = n
2h = n
h = log2n

즉, 각 레벨의 합인 n 이 트리의 높이인 logn 개 만큼 있으므로, 전체 시간 복잡도는 O(nlogn) 이라는 결론을 얻을 수 있다.


비교

Coursera 강의에 재미있는 자료가 있었다. Merge sort 와 insertion sort 를 일반 컴퓨터, 슈퍼 컴퓨터로 실행한 결과에 대한 비교이다. 삽입정렬은 N 이 10억개면 317년이 걸린다.

mergesort-vs-insertionsort
merge sort vs insertion sort

Good algorithms are better than supercomputers. – Robert Sedgewick 교수님 가라사대


참고한 자료

Comments

Yaboong's Picture

Yaboong

오스카 쉰들러는 흔해빠진 기회주의자요 부패한 사업가였다. 그러나 거대한 악이 세상을 점령하는 것처럼 보일 때 그 악에 대항해서 사람의 생명을 구한 것은 귀족도 지식인도 종교인도 아닌 부패한 기회주의자 오스카 쉰들러였다.

Seoul, South Korea https://github.com/yaboong