Algorithm
5. 분할과 정복_병합정렬(Merge Sort)
yeommm
2019. 10. 4. 02:02
#분할과 정복을 이용한 정렬
-병합정렬
-퀵정렬
#분할과 정복 알고리즘
1. 큰 문제를 작은 문제들로 나눈다.
2. 단계1의 작은 문제들의 해를 재귀적으로 구한다.
3. 단계2에서 구한 작은 문제들의 해를 이용하여 원래 문제의 해를 구한다.
#병합정렬 알고리즘
1. 주어진 크기가 n인 리스트를 크기가 n/2인 두 부분으로 나눈다.
2. 크기가 n/2인 두 부분을 재귀적으로 정렬한다.
3. 정렬된 두 부분을 하나로 병합한다.
#구현
#include <iostream>
using namespace std;
int* sorted;
void mergesort(int a[], int first, int last);
void merge(int a[], int first, int mid, int last);
int main(){
int size;
cin>>size;
sorted = new int[size]; //정렬 후 배열
int* a = new int[size]; //정렬 전 배열
for(int i=0; i<size; i++) cin>>a[i];
mergesort(a,0,size-1);
for(int i=0; i<size; i++) cout<<a[i]<<" ";
delete[] a;
return 0;
}
void mergesort(int a[], int first, int last){
if(first<last){
int mid=(first+last)/2;
mergesort(a,first,mid);
mergesort(a,mid+1,last);
merge(a,first,mid,last);
}
}
void merge(int a[], int first, int mid, int last){
int i=first; //왼쪽 배열의 첫 번째 인덱스
int j=mid+1; //오른쪽 배열의 첫 번째 인덱스
int k=first; //sorted의 첫 번째 인덱스
//두 집합의 원소들 비교해서 더 작은 원소를 sorted 배열에 넣기
while(i<=mid && j<=last){
if(a[i]<=a[j]){
sorted[k]=a[i];
i++;
}
else{
sorted[k]=a[j];
j++;
}
}
//남은 원소 sorted에 넣기
if(i>mid){ //오른쪽 원소는 모두 sorted에 담긴 상태
for(int t=j; t<=last; t++){
sorted[k]=a[t];
k++;
}
}
else{ //왼쪽 원소는 모두 sorted에 담긴 상태
for(int t=i; t<=mid; t++){
sorted[k]=a[t];
k++;
}
}
for(int t=first; t<=last; t++)
a[t]=sorted[t];
}
#시간복잡도
n=1인 경우 -> T(n) = 0
n>1인 경우 -> T(n) ≤ 2T(n/2) + n
≤ 2( 2T(n/4) + n )+n
...
≤ (2^k)T(n/(2^k)) + k*n
즉, n=2^k 일 경우, T(n) ≤ n*T(1) + (log n)*n = 0 + n log n
크기가 n인 배열을 분할할 때 원소가 하나가 될때까지 반으로 나누기 때문에 log n의 시간이 들고,
병합할 때 while을 통해 모든 원소를 비교하기 때문에 n의 시간이 든다.
따라서 병합정렬의 시간복잡도는 T(n): O(n log n) 이다.
#병합정렬의 단점
- 병합할 때 O(n) 추가적인 메모리가 필요하다.(in-place가 아니다)
- O(log n)의 스택 메모리가 필요하다.