파이썬의 자료구조 - 합병 정렬

앞에서 여러가지의 정렬을 알아보았는데 그 중 합병 정렬을 다뤄보겠다.

합병 정렬(Merge Sort)이란?

입력이 2개의 부분 문제로 분할되고, 부분 문제의 크기가 1/2로 감소하는 분할 정복 알고리즘
즉, n개의 숫자들을 n/2개씩 2개의 부분문제로 분할하고, 각각의 부분 문제를 순환적으로 합병 정렬한 후, 2개의 정렬된 부분을 합병하여 정렬하는 것.
이를 이용하여 합병 과정이 정복하는 것이라고 할 수 있다. 여기서 합병이란 2개의 각각 정렬된 숫자들을 1개의 정렬된 숫자들로 합치는 것이다.

합병 정렬의 과정

image

그림을 통해 합병 정렬 수행 과정을 알아보자.
맨 위에 원소의 개수가 8인 정렬되지 않은 배열이 있다. 저 배열을 정렬하기 위한 방법은 반으로 나누어 다시 합병정렬을 하는 것이다.
원소의 개수가 4 -> 2 -> 1 로 될때까지 반으로 나눈다.

이제 원소의 개수가 1이 되었으면 배열을 다시 합병(정복)해주면 된다.
두 원소의 크기를 비교해 작은 것부터 임시 배열에 넣고 (오름차순) 이 과정을 반복하면서 배열을 합치면서 병합한다.
왼쪽과 오른쪽을 정렬했다면, 이제 왼쪽 배열과 오른쪽 배열의 원소를 하나하나 비교해 작은 것부터 다시 넣는다.

마지막 과정을 한번 본다면 <원소의 개수 8> 일 때의 왼쪽 배열을 정렬한 것을 알 수 있다.
이제 <원소의 개수 8> 일 때의 오른쪽 배열(35, 13, 25, 24))도 위와 같은 과정을 거쳐 아래와 같이 정렬된다면,
이 왼쪽 배열(10, 22, 30, 37)과 비교해서 배열을 완성하게 된다.

이를 통해 병합 정렬을 마무리하였다.

한 마디로 요약하자면, 정렬되지 않은 배열을 받으면 그냥 원소의 개수가 1이 될 때까지 반으로 계속 나누다가, 원소의 개수가 1이 된다면 그때부터 나눠왔던 원소들의 값을 비교해서 합치는 것이다.

코드 구현

python을 이용하여 병합 정렬을 구현해보았다.

import sys
input = sys.stdin.readline
result = 0

def merge(s,e) : # s(시작),e(종료),m(중간)
    global result 
    if e - s < 1 :
        return
    m = int(s + (e-s) / 2)
    merge(s,m) # 재귀 함수로 리스트 나누기
    merge(m+1, e)
    for i in range(s, e+1):
        temp[i] = A[i] # 임시로 리스트 저장
    k = s
    
    # 두 그룹 나누고 병합하는 방식
    index1 = s
    index2 = m+1
    while index1 <= m and index2 <= e: # 양쪽의 인덱스를 비교하여 더 작은 수를 선택후 리스트에 저장
        if temp[index1] > temp[index2] :
            A[k] = temp[index2]
            result = result + index2 - k # 뒤쪽 데이터값이 더 작으면 결과값 업데이트
            k += 1
            index2 += 1 # 선택한 데이터 index값을 오른쪽으로 이동
        else :
            A[k] = temp[index1]
            k += 1
            index1 += 1
    while index1 <= m :
        A[k] = temp[index1]
        k += 1
        index1 += 1
    while index2 <= e :
        A[k] = temp[index2]
        k += 1
        index2 += 1

N = int(input())
A = list(map(int,input().split()))
A.insert(0, 0)
temp = [0] * int(N+1)
merge(1,N)
print(result)

Categories:

Updated:

Comments