8.4 퀵 정렬
- 분할 정복(Divide and Conquer)에 바탕을 둔 알고리즘
1) 기준 요소 선정 및 정렬 대상 분류
- 자료구조에서 기준이 될 요소를 임의로 선정하고 기준 요소(Pivot)의 값보다 작은 값을 가진 요소는 기준 요소의 왼쪽으로, 큰 값을 가진 요소는 오른쪽으로 옮김
2) 정렬대상 분할
- 기준 요소 왼쪽에는 기준 요소보다 작은 요소의 그룹, 오른쪽에는 큰 요소의 그룹이 생김
여기에서 왼쪽 그룹과 오른쪽 그룹을 분할하여 각 그룹에 대해 1)의 과정 수행
3) 반복
- 그룹의 크기가 1 이하여서 더 이상 분할할 수 없을 때까지 1), 2)의 과정을 반복하면 정렬이 완료
ex)
- 5를 기준으로 5보다 작은 요소를 왼쪽에, 큰 요소들은 오른쪽에 모음
- 5보다 작은 1,4,3,2는 왼쪽 그룹에, 5보다 큰 6,8,7,9는 오른쪽 그룹에 모이게 됨
- 첫 번째 요소인 1을 기준으로 선정
- 1,4,3,2 중 1보다 작은 요소는 없고 1보다 큰 요소는 모두 1의 오른쪽에 와 있으므로 이미 분할된 상태
- 4,3,2 중 첫 번째 요소인 4를 기준으로 선정하여 분할
- 분할 결과 4의 왼쪽 그룹에 3,2가 옮겨져 옴
- 3,2를 분할
- 3,2 중 첫 번째에 위치한 3을 서정하여 분할 수행
- 2는 3보다 작으므로 3의 왼쪽에 와야 함
- 3의 왼쪽 그룹은 크기가 1, 오른쪽 그룹은 크기가 0이므로 최초 분할에서 생긴 왼쪽 그룹이 정렬됨
- 오른쪽 그룹 분할 시작
- 6을 기준 요소로 선정했는데 6보다 작은 요소가 없음
-> 6의 오른쪽에 있는 그룹을 분할
- 8,7,9 중 첫 번째 요소인 8을 기준으로 선정하고 분할 수행
- 8의 왼쪽 그룹도 7 하나뿐이고 오른쪽 그룹도 9 하나밖에 안 남았으므로 추가적인 분할이 불가능한 상태
8.4.1 퀵 정렬 사용 전 해결해야 하는 2가지 문제
1) 배열을 사용할 경우 퀵 정렬의 분할 과정을 어떻게 효율적으로 처리할 것인가?
- 퀵 정렬 알고리즘 분할 과정에서 기준 요소보다 작은 요소는 기준 요소의 왼쪽으로, 큰 요소는 오른쪽으로 이동해야 함
- 배열에서 요소 사이에 새로운 요소를 삽입하려면 다른 요소를 이동시켜야 하는데 이때 오버헤드가 크다는 문제
- 요소의 이동을 최소화하기 위해 '수색 섬멸'작전
- 정찰병 2명이 1조로 움직이는데, 한 명은 왼쪽부터 오른쪽 방향으로 배열을 순회하면서 기준보다 큰 요소를 찾고, 다른 한명은 오른쪽부터 왼쪽 방향으로 순회하면서 기준보다 작은 요소를 찾음
- 이 두 정찰병이 찾아낸 두 요소를 맞교환함
- 정찰병들은 서로 접선할 때까지 교환해야 할 요소를 찾아내고 교환하는 과정을 이어 나감
- 이 작전은 두 정찰병이 접선하여 기준 요소를 왼쪽 그룹의 마지막 요소와 맞교환하는 것으로 끝남
2) 반복되는 분할 과정을 어떻게 처리할 것인가?
- 재귀호출
8.4.2 퀵 정렬 예제 프로그램
QuickSort() 함수
- DataSet 배열과, DataSet 내의 정렬 대상 범위 값 Left, Right를 매개변수로 받음
- Left와 Right는 수색섬멸을 수행하는 두 정찰병의 위치를 나타냄
- Left가 Right 보다 크거나 같으면 둘이 만났다는 뜻이므로 실행 종료
- Left가 Right보다 작으면 Partition() 함수를 실행한 후 QuickSort()를 재귀 호출
Partition() 함수
- 정렬 대상의 첫 번째 요소를 기준 요소(Pivot)으로 지정하고, while 루프를 통해 분할을 수행
- 첫 번째 While 루프는 DataSet의 왼쪽에서 출발해서 Pivot보다 큰 요소를 찾을 떄까지 탐색을 계속하며 Left를 1씩 증가시킴
- Pivot보다 큰 요소를 찾으면 Left에 그 요소가 위치한 인덱스를 저장하고 종료
- 두 번째 While 루프는 DataSet의 오른쪽에서 출발해서 Pivot보다 작은 요소를 찾아 그 요소의 위치를 Right에 저장
- 그런 다음 Left와 Right를 비교한 후 Left가 Right보다 작으면 두 정찰병이 찾은 요소들을 교환
- 분할 작업이 끝나면 기존 요소와 분할에 의해 왼쪽 자료구조의 오른쪽 끝에 새로 생긴 요소를 교환
- 마지막에 새 기준이 될 요소의 위치를 반환
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void Swap(int* A, int* B)
{
int Temp = *A;
*A = *B;
*B = Temp;
}
int Partition(int DataSet[], int Left, int Right)
{
int First = Left;
int Pivot = DataSet[First];
++Left;
while (Left <= Right)
{
while (DataSet[Left] <= Pivot && Left < Right)
++Left;
while (DataSet[Right] >= Pivot && Left <= Right)
--Right;
if (Left < Right)
Swap(&DataSet[Left], &DataSet[Right]);
else
break;
}
Swap(&DataSet[First], &DataSet[Right]);
return Right;
}
void QuickSort(int DataSet[], int Left, int Right)
{
if (Left < Right)
{
int Index = Partition(DataSet, Left, Right);
QuickSort(DataSet, Left, Index - 1);
QuickSort(DataSet, Index + 1, Right);
}
}
int main(void)
{
int DataSet[] = { 6,4,2,3,1,5 };
int Length = sizeof DataSet / sizeof DataSet[0];
int i = 0;
printf("정렬 전\n");
for (i = 0; i < Length; i++)
{
printf("%2d", DataSet[i]);
}
printf("\n");
QuickSort(DataSet, 0, Length - 1);
printf("정렬 후\n");
//정렬된 자료 구조 출력
for (i = 0; i < Length; i++)
{
printf("%2d", DataSet[i]);
}
printf("\n");
return 0;
}
'C언어 > 알고리즘' 카테고리의 다른 글
[알고리즘] ch 8. 정렬(1) - 버블 정렬, 삽입 정렬 (2) | 2023.11.23 |
---|---|
[알고리즘] ch 7. 그래프(4) - 최소 신장 트리( 크루스칼 알고리즘) (2) | 2023.11.06 |
[알고리즘] ch 7. 그래프 (3) - 최소 신장 트리(프림 알고리즘) (0) | 2023.11.06 |
[알고리즘] ch 7. 그래프 (2) - 위상 정렬 (0) | 2023.10.25 |
[알고리즘] ch 7. 그래프 (1) (0) | 2023.10.25 |