CS/알고리즘
[알고리즘] 병합정렬(Merge Sort)
안드선생
2023. 2. 20. 15:11
반응형
병합정렬(합병정렬) 이란?
비교 기반의 정렬로, 안정 정렬에 속하며 분할 정복(Divide and Conquer) 알고리즘 중 하나이다.
※ 분할 정복(Divde and Conquer) 이란?
: 그대로 해결할 수 없는 문제를 작은 문제로 분할하여 해결한 후, 결과를 모아 원래의 문제를 해결하는 전략.
병합정렬의 전체적인 과정
그래서, 병합정렬의 전체적인 과정은 다음과 같이 요약할 수 있다.
- 정렬되지 않은 리스트를 각각 1개의 원소만 포함하는 n개의 부분리스트로 분할한다.
- 분할된 부분리스트들을 병합(Merge)하며 정렬한다.
그럼 다시 결국 1개의 리스트가 될 것이고 이 리스트는 정렬된 상태일 것이다.
병합정렬의 세부 과정
- 분할(Divide) : 정렬되지 않은 리스트를 절반으로 분할하여 비슷한 크기의 두 리스트로 만든다.
모든 리스트의 길이가 1이 될 때 까지 반복해서 분할 한다. - 정복(Conquer) : 쪼개진 각 부분 리스트들을 정렬하며 합병해나간다.
- 결합(Combine) : 최종적으로 다시 하나의 정렬된 리스트로 결합한다.
이 과정을 그림으로 한번 살펴보자.
병합정렬 코드
그럼, 파이썬으로 나타낸 코드와 함께 살펴보자.
def merge_sort(list):
# 길이가 1이면(보다 작으면) 정렬된 것으로 봄.(정렬필요X)
if len(list) <= 1:
return list
# 두개의 분할 리스트로 쪼갬
mid = len(list) // 2
left = list[:mid]
right = list[mid:]
# 쪼갠 각각의 리스트에 대해 길이가 1이 될 때 까지 반복 수행(재귀)
left = merge_sort(left)
right = merge_sort(right)
# 쪼갠 리스트들을 정렬해가며 병합하는 함수 호출
return merge(left, right)
def merge(left, right):
# 정렬될 요소를 넣을 새 리스트
result = []
# 왼쪽, 오른쪽 리스트 모두 빌 때 까지 반복
while len(left) > 0 or len(right) > 0:
if len(left) > 0 and len(right) > 0:
# 첫 번째 값의 크기를 비교하여 작은 값을 꺼내 새 리스트에 삽입
if left[0] <= right[0]:
result.append(left[0])
left = left[1:]
else:
result.append(right[0])
right = right[1:]
# 한 리스트만 비었다면 남은 리스트의 값을 넣음
elif len(left) > 0:
result.append(left[0])
left = left[1:]
elif len(right) > 0:
result.append(right[0])
right = right[1:]
# 정렬된 하나의 리스트 반환
return result
병합정렬의 장단점
- 장점
- 안정 정렬(Stable Sort)로, 입력데이터가 무엇이든 시간복잡도는 O(nlogn)이다.
그래서, 항상 동일한 시간이 소요되므로 어떤 경우에도 좋은 성능을 보장한다.
-> 데이터의 분포에 영향을 덜 받는다.
- 안정 정렬(Stable Sort)로, 입력데이터가 무엇이든 시간복잡도는 O(nlogn)이다.
- 단점
- 제자리 정렬이 아니기 때문에, 별도의 추가적인 메모리가 필요하다.
- 데이터가 많은 경우에는 이동 횟수가 많으므로 큰 시간적 낭비를 초래한다.
시간복잡도
그럼 위 그림과 함께 어떻게 O(nlogn) 이라는 시간복잡도가 나왔는지 알아보자.
- 분할 단계 : 개수가 1 이 될 때 까지 쪼개기만 하므로 연산수행 X
- 병합 단계 :
- 병합 단계의 깊이
- 데이터의 개수 N이 2의 거듭제곱이라고 할 때,
N=8 (2^3) 이라면, 8 -> 4 -> 2 ->1 개로 줄어 단계가 3임을 알 수 있다.
N=16 (2^4) 이라면, 16 -> 8 -> 4 -> 2 -> 1 개로 줄어들어 4단계이다.
즉, n=의 경우, 깊이 k=임을 알 수 있다.
- 데이터의 개수 N이 2의 거듭제곱이라고 할 때,
- 비교 연산
- 마찬가지로, N이 8 이라고 할 때,
크기가 1인 부분 배열 2개를 합병하는데 최대 2번의 비교 연산이 필요하며
쌍이 4개 이므로 2 x 4 = 8번의 비교 연산이 필요하다. - 그 다음 단계에서는, 크기가 2인 부분배열 2개를 합병하는데 최대 4번의 비교연산이 필요하며
쌍이 2개 이므로 4 x 2 = 8번의 비교 연산이 필요하다.
즉, 각각의 병합 단계마다 최대 N번의 비교 연산을 수행한다.
- 마찬가지로, N이 8 이라고 할 때,
- 따라서 병합 단계의 깊이 x 각 단계의 비교 연산= 이다.
- 이를 빅오 표기법으로 표기하면 O(이다. )
- 병합 단계의 깊이
참고 자료
반응형