병합 정렬(Merge Sort)

2025. 1. 27.·알고리즘

병합 정렬(Merge Sort)

분할 정복 알고리즘

; 작게 나눠 정렬된 상태로 만든 뒤, 그 정렬된 결과들을 합치면서 전체 정렬을 완성하는 알고리즘

리스트를 절반씩 나누고, 각각 정렬한 뒤 병합(merge) 단계에서 두 정렬된 리스트를 하나로 합침

----🐢

동작 방식

분할(Divide)

리스트(또는 배열)를 반으로 나눔

정복(Conquer) / 재귀적 정렬

나눠진 리스트가 길이 1 이하가 될 때까지 계속 분할

길이가 1인 리스트는 이미 정렬된 상태로 볼 수 있음

병합(Merge)

분할된 리스트를 정렬된 상태로 합치며 올라옴

두 개의 정렬된 리스트(서브리스트)를 하나의 큰 정렬된 리스트로 병합

---🐢-

동작 과정 예시

[12, 11, 13, 5, 6, 7] 병합 정렬

분할

[12, 11, 13, 5, 6, 7]를 두 부분으로 나누기

 왼쪽: [12, 11, 13]

 오른쪽: [5, 6, 7]

각각을 다시 두 부분으로 나누기 (재귀)

 왼쪽 [12, 11, 13]

  [12] / [11, 13]

  [11, 13] → [11] / [13]

 오른쪽 [5, 6, 7]

  [5] / [6, 7]

  [6, 7] → [6] / [7]

이 과정을 반복해 모두 길이 1 이하 리스트가 됨

병합

이제 분할이 끝났으니 길이 1인 리스트들을 정렬된 상태로 합침

예: [11] 과 [13] → [11, 13]

[12] 와 [11, 13] → 이 두 리스트를 정렬된 순서로 합치면 [11, 12, 13]

오른쪽도 동일: [6] 과 [7] → [6, 7], 그 뒤 [5] 와 [6, 7] → [5, 6, 7]

마지막으로 [11, 12, 13] 과 [5, 6, 7] 를 병합 → [5, 6, 7, 11, 12, 13]

 

# 병합 과정에서 두 개의 정렬된 서브리스트를 하나로 합칠 때는, 각 서브리스트에 대해 포인터를 두고, 두 리스트의 현재 요소를 비교하여 더 작은 것을 결과 리스트에 삽입한 뒤 포인터를 이동하는 방식을 사용

--🐢--

코드 예시

def merge_sort(arr):
    # 리스트 길이가 1 이하이면 이미 정렬된 상태
    if len(arr) > 1:
        mid = len(arr) // 2
        left_half = arr[:mid]
        right_half = arr[mid:]

        # 분할 -> 재귀로 각각 정렬
        merge_sort(left_half)
        merge_sort(right_half)

        # 병합(Merge) 단계
        i = j = k = 0
        
        # 두 리스트의 각 요소를 비교하며 병합
        while i < len(left_half) and j < len(right_half):
            if left_half[i] < right_half[j]:
                arr[k] = left_half[i]
                i += 1
            else:
                arr[k] = right_half[j]
                j += 1
            k += 1

        # 남은 요소들 처리
        while i < len(left_half):
            arr[k] = left_half[i]
            i += 1
            k += 1

        while j < len(right_half):
            arr[k] = right_half[j]
            j += 1
            k += 1

if __name__ == "__main__":
    data = [12, 11, 13, 5, 6, 7]
    merge_sort(data)
    print("정렬 결과:", data)
    # 정렬 결과: [5, 6, 7, 11, 12, 13]

길이 1 이하일 때까지 분할 (재귀)

분할된 부분 리스트들이 정렬된 상태로 돌아온 뒤, merge 단계에서 정렬된 하나의 리스트로 합침

병합 시 두 포인터(i, j)를 사용해, 더 작은 원소를 메인 배열(arr[k])에 복사하고 포인터 이동

-🐢---

시간 복잡도

분할: 매번 리스트를 절반으로 나누므로 log n번 분할

병합: 각 단계에서 리스트 전체 원소를 한 번씩 확인하므로 O(n)

결합: O(n) * O(log⁡ n) = O(n log⁡ n)

최악/평균/최선: 모두 O(n log⁡ n)

; 이는 어떤 형태의 입력이 와도 분할과 병합이 동일하게 수행되기 때문

🐢----

장단점

장점

최악/평균/최선 모든 경우에 O(n log n)

안정 정렬: 중복 원소가 있을 때, 입력에서의 순서가 출력 순서로 유지

리스트가 큰 경우나 분산된 메모리에 있을 경우, 분할 정복 특성상 성능이 좋을 수 있음

단점

추가 메모리(공간 복잡도 O(n))가 필요

작은 배열을 쪼개도 병합 절차가 발생

→ 상수 계수(overhead)가 크며, 실제로는 퀵 정렬보다 다소 느릴 수 있음

인플레이스 구현(추가 공간 없이)은 일반적으로 어렵고 복잡

저작자표시 비영리 변경금지 (새창열림)

'알고리즘' 카테고리의 다른 글

그리디 알고리즘  (0) 2025.02.04
탐색 알고리즘  (0) 2025.02.03
힙 정렬(Heap Sort)  (0) 2025.01.31
퀵 정렬(Quick Sort)  (0) 2025.01.26
버블/선택/삽입 정렬  (1) 2025.01.24
'알고리즘' 카테고리의 다른 글
  • 탐색 알고리즘
  • 힙 정렬(Heap Sort)
  • 퀵 정렬(Quick Sort)
  • 버블/선택/삽입 정렬
우는거북이
우는거북이
  • 우는거북이
    거북이는 울고 있다
    우는거북이
  • 전체
    오늘
    어제
    • 알아보기 (78)
      • AI (4)
      • 언어 (16)
        • Python (15)
        • C언어 (1)
      • 알고리즘 (7)
      • 백준 (22)
      • 자료구조 (10)
      • 컴퓨터네트워크 (6)
      • 운영체제 (1)
      • 데이터통신 (12)
  • 인기 글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.0
우는거북이
병합 정렬(Merge Sort)
상단으로

티스토리툴바