Merge sort
하나의 리스트를 두개의 균등한 크기로 분할하고 분할된 부분 리스트를 정렬한 다음, 두 개의 정렬된 부분 리스트를 병합하여 전체가 정렬된 리스트가 되게 하는 정렬 방법이다. 합병 정렬은 분할 정복(Devide and conquer) 방법에 바탕을 두고 있다.
- 분할(Devide) : 정렬하려는 리스트를 같은 크기의 2개의 부분 배열로 분할한다.
- 정복(Conquer) : 부분 배열을 정렬한다. 부분 배열의 크기가 충분히 작을때까지 재귀 호출을 이용하여 1단계-2단계를 반복한다.
- 결합(Combine) : 정렬된 부분 배열들을 하나의 배열로 합병한다.
이해를 돕기 위한 애니메이션 영상!!
구현
(업로드 예정입니다!!)
def merge(leftList, rightList):
i = 0
j = 0
sorted_list = []
while(i<len(leftList) and j<len(rightList)):
if leftList[i] < rightList[j]:
sorted_list.append(leftList[i])
i+=1
else:
sorted_list.append(rightList[j])
j+=1
#남은값들 다 넣어주기
while(i<len(leftList)):
sorted_list.append(leftList[i])
i+=1
while(j<len(rightList)):
sorted_list.append(rightList[j])
j+=1
return sorted_list
def mergeSort(origin):
# base case
if len(origin) <= 1:
return origin
mid = len(origin) // 2
leftList = origin[:mid]
rightList = origin[mid:]
leftList = mergeSort(leftList)
rightList = mergeSort(rightList)
return merge(leftList, rightList)
print(mergeSort([14, 7, 3, 12, 9, 11, 6, 2]))
시간 복잡도 분석
예를 들어, 리스트의 크기가 N($2^3)$일때 순환 호출의 깊이 k 는 $log_2N$($log_23$)이 된다. ($n = 2^k$)
배열이 부분배열로 나눠질때는 인덱스를 기준으로 단순하기 나누기 때문에 부분 배열이 합쳐지는 결합단계에서 시간 복잡도를 체크하면된다.
하나의 합병 단계에서 최대 n번의 비교 연산이 필요하다. 결과적으로 merge 단계가 $log_2N$만큼 있고 원소의 크기를 비교하는 비교 연산이 최대 N번 있을수있기 때문에 $Nlog_2N$ 번 필요하다.
합병 정렬의 장점은 안정적인 정렬 방법으로 데이터의 분포에 영향을 덜 받는다. 즉 입력 데이터가 무엇이든 간에 정렬되는 시간이 동일하다. 최악,평균,최선의 경우가 모두 $O(Nlog_2N)$ 이다.
참고
합병 정렬을 생성할때 임시 배열을 생성하여 부분 배열을 저장한다. 또한 레코드의 크기가 클 경우 이동 횟수가 많을수있다. 이때 각각의 레코드를 연결리스트로 구성하여 합병 정렬을 하면 연결리스트 각각이 가리키는 포인터(링크 인덱스)가 달라지기 때문에 퀵정렬을 포함하여 다른 어떤 정렬 방법보다도 효율적일 수 있다.
'Algorithm' 카테고리의 다른 글
[정렬 알고리즘] - Quick Sort (0) | 2021.02.03 |
---|---|
[SWEA] JAVA - 1208.Flatten (0) | 2021.02.02 |
[백준] JAVA - 1244.스위치 켜고 끄기 (0) | 2021.02.01 |
[백준] JAVA - 1535.안녕 (0) | 2021.01.30 |
[Programmers] 더 맵게 (0) | 2021.01.29 |