728x90
합병 정렬은 분할 정복 알고리즘을 활용한 정렬 방법이다. 먼저 원소들을 반으로 나누고 두 개의 부분 배열을 비교하여 정렬된 상태로 만들어 가는 것이다.
개념
- 오름차순 기준으로 설명한다.
- N개의 원소가 있을 때 N//2를 기준으로 원소들을 반으로 나눈 값을 인덱스로 하고
- 위에서 구한 인덱스를 기준으로 리스트를 반으로 나눈다.
- 반으로 나눈 리스트를 또 다시 반으로 나누고 리스트 원소의 개수가 1일 때 해당 원소를 반환한다.
- 이렇게 반으로 나눈 원소들을 합쳐 나간다.
- 초기에 1개의 원소들로 이루어진 리스트를 비교하여 정렬 후에 2개의 원소를 갖는 리스트로 만든다.
- 이후에는 2개의 원소를 갖는 리스트끼리 비교하여 정렬하여 4개의 원소를 갖는 리스트로 만든다.
- 위의 과정을 반복하는 것이 합병 정렬이다.
구현
- merge_sort 함수는 최종적으로 정렬된 리스트를 반환한다.
- 함수 내에서 전달받은 numbers를 기준으로 반으로 나눈다.
- 반으로 나눈 리스트를 다시 merge_sort로 전달하여 반으로 나눈다.
- 이때 numbers가 한 개의 원소를 갖을 때를 기저 사례로 설정한다.
- 이제는 merge 함수에 전달하여 정렬된 리스트로 반환하도록 한다.
def merge_sort(numbers):
if len(numbers) < 2:
return numbers
mid = len(numbers) // 2
left_numbers = numbers[:mid]
right_numbers = numbers[mid:]
left_numbers = merge_sort(left_numbers)
right_numbers = merge_sort(right_numbers)
return merge(left_numbers, right_numbers)
- 두 개의 리스트를 전달받는다.
- 두 개의 리스트에서 각각 첫 번째 원소들을 비교하여 더 작은 수를 result에 추가한다.
- 만약 한 쪽의 리스트를 모두 result에 추가했다면 다른 쪽의 리스트의 원소들을 result에 순차적으로 추가한다.
- 이 과정을 통해 정렬된 상태를 반환할 수 있게 된다.
def merge(left_numbers, right_numbers):
result = []
while left_numbers or right_numbers:
if left_numbers and right_numbers:
if left_numbers[0] < right_numbers[0]:
result.append(left_numbers[0])
left_numbers = left_numbers[1:]
else:
result.append(right_numbers[0])
right_numbers = right_numbers[1:]
elif left_numbers:
result.append(left_numbers[0])
left_numbers = left_numbers[1:]
elif right_numbers:
result.append(right_numbers[0])
right_numbers = right_numbers[1:]
return result
특징
- 입력 데이터와 관계없이 정렬되는 시간이 동일한다.
- 정렬해야 할 데이터가 클 경우에는 이동 횟수가 많아지기 때문에 시간적 낭비가 발생한다.
시간 복잡도
리스트 내에 원소가 한 개 남을 때까지 분할 할 때 N번 재귀 호출이 발생하며 리스트를 반씩 정렬하면서 합쳐지기 때문에 logN의 시간이 발생한다. 따라서 NlogN만큼의 시간이 소요된다.
'CS > 알고리즘' 카테고리의 다른 글
[알고리즘] 크루스칼 (0) | 2021.10.11 |
---|---|
[알고리즘] 유니온 파인드 (Union - Find) (0) | 2021.10.05 |
[알고리즘] 슬라이딩 윈도우 (0) | 2021.09.28 |
[알고리즘] 정렬 : 삽입정렬 (0) | 2021.09.26 |
[알고리즘] 정렬 : 선택정렬 (0) | 2021.09.24 |