Devide and conquer
알고리즘이다.Devide and conquer (분할 정복법): 기존의 문제를 해결하기 쉬운 단위로 나누어 해결한 후 다시 합쳐서 해를 구하는 방법
void quick_sort(int left, int right) {
if(left < right) {
int pivot_index = partition(left, right);
quick_sort(left, pivot_index-1);
quick_sort(pivot_index+1, right);
}
}
int partition(int left, int right) {
int low = left;
int high = right + 1;
int pivot = arr[left];
do{
do{
low++;
} while(low <= right && arr[low] < pivot);
do{
high--;
} while(high >= left && arr[high] > pivot);
if(low < high)
swap(arr[low], arr[high]);
} while(low < high);
swap(arr[left], arr[high]);
return high;
}
최선, 평균(배열이 균등하게 분할될 때):
=> O(nlogn)
최악(pivot이 배열의 최소값이거나 최대값이라서 비균등 분할이 계속될 때):
=> O(n^2)
제자리 정렬: 배열 이외에 추가 메모리를 요구하지 않는 정렬 방법
불안정 정렬: 동일한 값을 가진 데이터의 순서가 유지되지 않는 정렬 방법
Devide and conquer
알고리즘이다.void merge_sort(int low, int high) {
if(high - low < 2) return;
int mid = (low+high) / 2;
merge_sort(low, mid);
merge_sort(mid+1, high);
merge(low, mid, high);
}
void merge(int low, int mid, int high) {
int[] temp = new int[high - low];
int t = 0, l = low, h = mid;
while (l < mid && h < high) {
if(arr[l] <= arr[h]) {
temp[t++] = arr[l++];
} else {
temp[t++] = arr[h++];
}
}
while (l < mid) {
temp[t++] = arr[l++];
}
while (h <= high) {
temp[t++] = arr[h++];
}
for (int i = low; i <= high; i++) {
arr[i] = temp[i];
}
}
비교 횟수: 재귀 호출의 깊이가 logn이고 각 합병 단계에서 비교 연산이 n번 일어나므로 nlogn이다.
이동 횟수: 재귀 호출의 깊이가 logn이고 각 합병 단계에서의 이동 연산은 임시 배열에 복사했다가 다시 가져와야 하므로 총 2n번 발생한다. 따라서 2nlogn이다.
nlogn(비교) + 2nlogn(이동) = 3nlogn =>
O(nlogn)
안정 정렬: 동일한 값을 가진 데이터의 순서가 유지되는 정렬 방법
병합 정렬은 분할이 균등하게 (절반씩) 일어나지만 퀵 정렬은 불균등하게 일어난다.
퀵 정렬은 최악의 경우를 방지하기 위한 다양한 전략이 존재한다.
병합 정렬은 순차적인 비교를 하므로 연결리스트를 정렬할 때 효과적이다. 이동 연산도 링크 인덱스만 변경하면 되므로 효율적이다.
퀵 정렬은 데이터에 임의 접근을 하기 때문에 연결리스트를 정렬하기에 비효율적이다.