Quick Sort
- 분할 정복(divide-and-conquer) 방식의 정렬 알고리즘의 일종.
- 주어진 배열에서 피벗(pivot) 이라는 분할의 기준이 되는 요소를 선택하고, 이를 기준으로 배열을 두 개의 부분 배열(파티션) 로 나누어 정렬하는 방식으로 작동한다.
특징
-
시간 복잡도 : 평균적으로 O(n log n) 의 시간 복잡도를 가지지만(n 번 파티션을 나누고, 나눌때마다 탐색 범위가 절반이 되므로), 최악의 경우 O(n^2) (선택하는 모든 피벗이 가장 작은 값일 때) 이 될 수 있다.
-
메모리 사용 : 퀵 정렬은 정렬을 할 때, 추가적인 메모리가 필요없는 제자리(in-place) 정렬로, 추가적인 메모리 사용이 최소화된다.
단계
1. 피벗 선택 과 배열 분할 (분할)
배열에서 임의의 요소를 피벗으로 선택한다. 따라서, 피벗은 첫 번째 요소, 마지막 요소, 중간 요소, 또는 배열 내의 여러 요소 중 하나일 수 있다.
2. 정렬 (정복)
피벗을 기준으로 배열의 요소를 정렬한다. 피벗보다 작은 요소는 피벗의 왼쪽에, 큰 요소는 오른쪽에 위치하게 한다.
정렬하는 방식은 다음과 같다.
left
(혹은start
),right
(혹은end
) 포인트를 피벗과 비교한다.left
가 가리키는 값이 피벗보다 작다면, 무시하고 포인터를 다음으로 이동한다.left
가 가리키는 값이 크다면,right
포인터가 가리기키는 값과 피벗을 비교한다.right
가 가리키는 값이 피벗보다 크다면, 무시하고 포인터를 다음으로 이동한다.right
가 가리키는 값이 피벗보다 작다면,left
가 가리키는 값과 교체(swap) 한다.
left
와right
가 엇갈린다면, 정렬을 멈춘다.
결합(Combine)
정렬된 부분 배열들을 하나의 배열에 합병한다.
# include <stdio.h>
# define MAX_SIZE 9
# define SWAP(x, y, temp) ( (temp)=(x), (x)=(y), (y)=(temp) )
// 1. 피벗을 기준으로 2개의 부분 리스트로 나눈다.
// 2. 피벗보다 작은 값은 모두 왼쪽 부분 리스트로, 큰 값은 오른쪽 부분 리스트로 옮긴다.
/* 2개의 비균등 배열 list[left...pivot-1]와 list[pivot+1...right]의 합병 과정 */
/* (실제로 숫자들이 정렬되는 과정) */
int partition(int list[], int left, int right){
int pivot, temp;
int low, high;
low = left;
high = right + 1;
pivot = list[left]; // 임의의 값을 피벗으로 선택(이 경우, 배열의 첫번째 요소를 피벗으로 선택.)
/* low와 high가 교차할 때까지 반복(low < high) */
do {
/* list[low]가 피벗보다 작으면 계속 low를 증가 */
do {
low++; // low는 left+1 에서 시작
} while (low <= right && list[low] < pivot);
/* list[high]가 피벗보다 크면 계속 high를 감소 */
do {
high--; //high는 right 에서 시작
} while (high >= left && list[high] > pivot);
// pivot 과 비교하여 값이 크거나 작은 시점.
// 만약 low와 high가 교차하지 않았으면 list[low]를 list[high] 교환
if(low < high){
SWAP(list[low], list[high], temp);
}
} while (low < high);
// low와 high가 교차했으면(엇갈렸다면)ㅣ 반복문을 빠져나와 list[left]와 list[high]를 교환
SWAP(list[left], list[high], temp);
// 피벗의 위치인 high를 반환
return high;
}
// 퀵 정렬
void quick_sort(int list[], int left, int right){
if(left < right){
int pivot = partition(list, left, right); // 피벗의 위치
// 피벗은 제외한 2개의 부분 리스트를 대상으로 순환 호출
quick_sort(list, left, pivot - 1); // left 부터 피벗 바로 앞 정렬
quick_sort(list, pivot + 1, right); // 피벗 바로 뒤 부터 right 뒤 정렬
}
}
void main(){
int i;
int n = MAX_SIZE;
int list[n] = {5, 3, 8, 4, 9, 1, 6, 2, 7};
quick_sort(list, 0, n-1);
}