참고 문헌 :
퀵소트 알고리즘 :: 마이구미
이 글은 정렬 중 퀵소트(Quick Sort), 퀵정렬이라고 불리는 정렬을 다뤄본다. 누구나 한번쯤 들어봤고, 구현해본 정렬 중 하나이다. 빠른 정렬에 분류되고 기본적인 버블 정렬처럼 많이 언급되는 정
mygumi.tistory.com
blog.naver.com/ndb796/221226813382
5. 퀵 정렬(Quick Sort)
지난 시간까지 다루었던 선택 정렬, 버블 정렬, 삽입 정렬 알고리즘은 모두 시간 복잡도 O(N^2)을 가지는...
blog.naver.com
gmlwjd9405.github.io/2018/05/10/algorithm-quick-sort.html
[알고리즘] 퀵 정렬(quick sort)이란 - Heee's Development Blog
Step by step goes a long way.
gmlwjd9405.github.io
1. 퀵 소트 개념 정리
- 분할 정복(Divide and Conquer)을 통하여 구현합니다.
- 시간 복잡도는 최악의 경우 O(N ^ 2)이고 평균적으로 O(NlogN)을 가집니다.
- 합병 정렬(merge sort)과 다르게 비 균등하게 정렬합니다.
2. 퀵 소트 정렬 과정
1) 배열의 원소 하나를 선택합니다. 이를 피벗(pivot)이라고 합니다.
2) 피벗을 기준으로 오른쪽에는 피벗보다 큰 수를 왼쪽으로 작은 수를 옮깁니다.(오름 차순으로 정렬 시)
3) 피벗을 제외하고 왼쪽, 오른쪽 배열을 분할합니다.
4) 분할 한 배열의 크기가 0 또는 1이 아닌 경우 1 ~ 3을 반복합니다.
ex)
1 | 4 | 3 | 5 | 2 |
를 정렬한다고 하면 과정은 다음과 같습니다. ( 주황색으로 채워진 칸은 정렬 완료 상태를 의미합니다. )
1번) pivot 선택하기 : pivot = 3(랜덤 선택)
2번) 정렬하기
1 | 2 | 3 | 5 | 4 |
3번) 배열 분할
left :
1 | 2 |
right :
5 | 4 |
4번) left 만족, right 만족
left - 1번) pivot 선택 : pivot = 1(랜덤 선택)
left - 2번) 정렬하기
1 | 2 |
left - 3번) 배열 분할하기
left_left
NULL |
left_right
2 |
left - 4번) left_left 불만족, left_right 불만족 => left 종료
현재 배열 상태
1 | 2 | 3 | 5 | 4 |
right - 1번) pivot 선택 : pivot = 5(랜덤 선택)
right - 2번) 정렬하기
4 | 5 |
right - 3번) 배열 분할하기
right_left
4 |
right_right
NULL |
right - 4번) right_left 불만족, right_right 불만족 => right 종료
현재 배열 상태
1 | 2 | 3 | 4 | 5 |
----- 더 이상 분할할 공간이 없으므로 정렬 종료 -----
최종 정렬 결과
1 | 2 | 3 | 4 | 5 |
pivot을 선택할 때 랜덤이라는 말을 사용하였는데 구현의 편리함을 위하여 보통 배열의 첫 번째 데이터를 pivot으로 선택합니다.
3. 퀵 소트 구현하기
코드는 다음과 같습니다.
C99
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
void quick_sort(int arr[], int s, int e) {
//--- 1) select the pivot ---//
int pivot = s;
int left = s + 1, right = e, tmp;
//--- 2) sort the Array ---//
while (left <= right) {
while (left <= e && arr[pivot] >= arr[left])
left++;
while (s < right && arr[pivot] <= arr[right])
right--;
tmp = arr[right];
if (left > right) {
arr[right] = arr[pivot];
arr[pivot] = tmp;
pivot = right;
}
else {
arr[right] = arr[left];
arr[left] = tmp;
}
}
//--- 3, 4) Check Array size and split Array ---//
if (pivot - s > 0)
quick_sort(arr, s, pivot - 1);
if (e - pivot > 0)
quick_sort(arr, pivot + 1, e);
}
int main(void) {
srand((unsigned int)time(NULL));
int arr[10] = {0, };
for (int i = 0; i < 10; i++)
arr[i] = rand() % 10 + 1;
printf("before sort : ");
for (int i = 0; i < 10; i++)
printf("%2d ", arr[i]);
quick_sort(arr, 0, 9);
printf("\n after sort : ");
for (int i = 0; i < 10; i++)
printf("%2d ", arr[i]);
return 0;
}
4. 추가로
퀵 소트의 최악의 시간 복잡도가 O(N ^ 2)이라고 되어있습니다.
이는 바로 이미 정렬된 배열을 정렬할 때 최악의 시간 복잡도를 가지게 됩니다. 이를 해결하는 방법으로는 배열의 중간 값을 선택하면 됩니다.
코드는 다음과 같습니다.
C99
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
void quick_sort(int arr[], int s, int e) {
//--- 1) select the pivot ---//
int pivot = s;
int left = s + 1, right = e, tmp;
//--- *) code Improvement ---//
tmp = arr[(s + e) / 2];
arr[(s + e) / 2] = arr[s];
arr[s] = tmp;
//--- 2) sort the Array ---//
while (left <= right) {
while (left <= e && arr[pivot] >= arr[left])
left++;
while (s < right && arr[pivot] <= arr[right])
right--;
tmp = arr[right];
if (left > right) {
arr[right] = arr[pivot];
arr[pivot] = tmp;
pivot = right;
}
else {
arr[right] = arr[left];
arr[left] = tmp;
}
}
//--- 3, 4) Check Array size and split Array ---//
if (pivot - s > 0)
quick_sort(arr, s, pivot - 1);
if (e - pivot > 0)
quick_sort(arr, pivot + 1, e);
}
int main(void) {
srand((unsigned int)time(NULL));
int arr[1000] = {0, };
for (int i = 0; i < 1000; i++)
arr[i] = rand() % 10 + 1;
printf("before sort : ");
for (int i = 0; i < 1000; i++)
printf("%2d ", arr[i]);
quick_sort(arr, 0, 999);
printf("\n after sort : ");
for (int i = 0; i < 1000; i++)
printf("%2d ", arr[i]);
return 0;
}
'개념 정리 > 알고리즘' 카테고리의 다른 글
두 포인터 알고리즘 (Two pointers method) (0) | 2021.06.28 |
---|---|
Greedy Algorithm (그리디, 탐욕 알고리즘) (0) | 2021.06.25 |
Merge Sort (합병 정렬) (0) | 2021.06.23 |
크루스칼 알고리즘 & 프림 알고리즘 (0) | 2021.06.15 |
Dynamic Programming : Knapsack Problem (0) | 2021.02.20 |