컴퓨터/알고리즘

[알고리즘] 병합정렬 알고리즘 | Merge Sort | C++코드 해석

우유식빵 2020. 2. 20. 04:01

분할정복(Divide and Conquer) 알고리즘을 공부하고있습니다.

병합정렬을 이해해보겠습니다, 우선은 코드는 이렇게 생겼습니다

#include <iostream>
using namespace std;
void merge(int* A,int begin, int mid, int end, int* B){ // 분할해둔 배열의 값을 크기비교하여 정렬하고 합친다.
int i,j,k;
i=begin;
j=mid;
for(k=begin; k<=end; k++){//정렬하면서 배열B에 차례로 넣어 줄 것.
// 분할된 블럭중 i는 왼쪽블럭담당, j는 오른쪽 블럭담당이다.
// j가 end보다 커졌다는 의미는 i담당인 왼쪽블럭만 남았다는 이야기이다.
// if가 true일땐 왼쪽블럭에서 배열B에 저장
if (i<mid&&(j>end||A[i]<=A[j])) {
B[k] = A[i];
i = i + 1;
// if가 else일땐 오른쪽블럭에서 배열B에 저장
} else {
B[k] = A[j];
j = j + 1;
}
}
for(k=begin; k<=end; k++){ // update
A[k]=B[k];
}
}
void split(int* A, int begin, int end, int* B){ //재귀함수를 이용해 분할한다.
int mid;
if(end-begin<1) return; //한 조각까지 분리됐으면 끝
mid=begin+(end-begin)/2;
split(A,begin,mid,B);
split(A,mid+1,end,B);
merge(A,begin,mid+1,end,B);
}
int main(){
int N; // 수열의 길이
cin>>N;
int* A = new int[N]; // 정렬 전 배열
int* B = new int[N]; // 정렬 하면서 결과물을 넣을 배열
for(int i=0; i<N; i++){
cin>>A[i];
}
split(A,0,N-1,B); // 배열이 0 부터 시작하므로 end에는 N-1이 들어가야한다.
//결과물 확인
for(int i=0; i<N; i++){
cout<<A[i]<<" ";
}
}
view raw MergeSort.cpp hosted with ❤ by GitHub
q
view raw q hosted with ❤ by GitHub

코드를 이해하기 쉽도록 병합정렬의 개념과 예시그림을 설명하겠습니다!

 

병합정렬은 분할과 정렬로 나눌수있습니다.

먼저 처음과 끝의 중간에서 두 개로 분할합니다.

이후 분할된 블록들을 계속해서 최소단위까지 분할해줍니다.

 

8개의 수열을 분할하는 과정을 예로 들면, 

이런 식이 됩니다.

코드를 보면 이에 재귀함수를 이용하였는데요, 빨간색 숫자는 재귀함수에 따라 분할이 되는 순서입니다.

재귀함수가 머리아프신분들은 저 순서를 보시면 직관적으로 바로 이해가 되실 거에요! 

 

분할이 끝났으면 5번에서 정렬&병합 한번, 8번에서 한번,

8번 병합이끝나고 6번에 돌아와서 한번, 6번에서 끝나고 2번에서 한번... 이런식으로 이루어집니다.

 

5번 분할이 끝난후 정렬&병합되는 과정은

이렇습니다. i는 왼쪽 수열, j는 오른쪽 수열의 포인터를 담당한다고 생각하면됩니다.

한칸짜리로는 각각 i와 j가 어떤 역할을 하는지 바로 알기 어려움으로 

 

8번 병합이 끝난후 6번에서 이루어지는 정렬&병합을 살펴보겠습니다

코드에 써있듯이, 왼쪽블럭과 오른쪽블럭을 비교하며 작은쪽이 임시배열인 B에 차례차례 들어가게됩니다.

B에 채워넣은다음에는 채워넣어진 쪽의 포인터가 한칸 전진하게됩니다. 

 

이러한 전체적인 과정을 움짤로 보자면,

출처: https://namu.wiki/w/%EC%A0%95%EB%A0%AC%20%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98#s-2.2.1

 

이런식이 됩니다 ;)