퀵 정렬(Quick Sort)
퀵 정렬(Quick Sort)은 평균적으로 가장 빠르게 동작하는 정렬 알고리즘으로 알려져 있으며, 분할 정복(Divide and Conquer) 전략을 기반으로 합니다. 리스트에서 기준 값(피벗, pivot)을 정한 후, 피벗보다 작은 값과 큰 값을 나누어 각각을 재귀적으로 정렬하는 방식입니다.
이 알고리즘은 in-place 정렬이 가능하고, 평균 시간 복잡도가 O(n log n)으로 매우 우수하기 때문에 실무에서도 널리 사용되며, 다양한 언어의 표준 라이브러리에도 활용됩니다. 다만, 피벗 선택이 좋지 않으면 최악의 경우 O(n²)까지 성능이 떨어질 수 있어 그에 대한 보완 전략도 함께 알아둘 필요가 있습니다.
1. 퀵 정렬이란?
퀵 정렬은 기준이 되는 값을 하나 정한 뒤, 그 값을 기준으로 작은 값과 큰 값을 나누고, 나뉜 그룹을 다시 같은 방식으로 정렬해나가는 알고리즘입니다. 이 과정을 반복하다 보면 전체 데이터가 정렬된 상태가 됩니다.
작동 방식
- 리스트에서 하나의 피벗(Pivot)을 선택
- 리스트를 피벗을 기준으로 작은 값과 큰 값으로 분할
- 각각의 부분 리스트에 대해 퀵 정렬을 재귀적으로 수행
- 모든 분할이 완료되면 리스트는 정렬된 상태가 됨
장점
- 평균적으로 매우 빠름 (O(n log n))
- 메모리 사용이 적은 제자리 정렬
- 대부분의 실무 상황에서 성능 우수
단점
- 불안정 정렬 (같은 값의 상대적 순서가 바뀔 수 있음)
- 최악의 경우 O(n²)
- 재귀 깊이가 깊어지면 스택 오버플로우 발생 가능
2. 예시
초기 배열 : [5, 3, 8, 4, 2, 7, 1, 6]
- 피벗 : 5
- 피벗보다 작은 값들 : [3, 4, 2, 1]
피벗보다 큰 값들 : [8, 7, 6] - 이 두 그룹을 다시 퀵 정렬 → 재귀적으로 진행
- 정렬 완료 후 합치면 : [1, 2, 3, 4, 5, 6, 7, 8]
3. 언어별 구현 예제
a. Python (재귀적 구현)
def quick_sort(arr):
if len(arr) <= 1:
return arr
pivot = arr[0]
left = [x for x in arr[1:] if x <= pivot]
right = [x for x in arr[1:] if x > pivot]
return quick_sort(left) + [pivot] + quick_sort(right)
b. C++
int partition(vector<int>& arr, int low, int high) {
int pivot = arr[high];
int i = low - 1;
for (int j = low; j < high; j++) {
if (arr[j] <= pivot) {
i++;
swap(arr[i], arr[j]);
}
}
swap(arr[i + 1], arr[high]);
return i + 1;
}
void quickSort(vector<int>& arr, int low, int high) {
if (low < high) {
int pi = partition(arr, low, high);
quickSort(arr, low, pi - 1);
quickSort(arr, pi + 1, high);
}
}
c. Java
int partition(int[] arr, int low, int high) {
int pivot = arr[high];
int i = low - 1;
for (int j = low; j < high; j++) {
if (arr[j] <= pivot) {
i++;
int temp = arr[i]; arr[i] = arr[j]; arr[j] = temp;
}
}
int temp = arr[i+1]; arr[i+1] = arr[high]; arr[high] = temp;
return i + 1;
}
void quickSort(int[] arr, int low, int high) {
if (low < high) {
int pi = partition(arr, low, high);
quickSort(arr, low, pi - 1);
quickSort(arr, pi + 1, high);
}
}
d. JavaScript
function quickSort(arr) {
if (arr.length <= 1) return arr;
const pivot = arr[0];
const left = arr.slice(1).filter(v => v <= pivot);
const right = arr.slice(1).filter(v => v > pivot);
return [...quickSort(left), pivot, ...quickSort(right)];
}
4. 시간 복잡도 분석
케이스 | 시간 복잡도 |
최선 | O(n log n) |
평균 | O(n log n) |
최악 | O(n²) |
공간 복잡도 | O(log n) (재귀 깊이) |
정렬 안정성 | 불안정 정렬 |
- 최악의 경우 : 이미 정렬된 상태에서 피벗을 가장 작거나 큰 값으로 선택할 때 발생합니다.
- 피벗 선택을 무작위로 하거나 Median of Three 전략을 사용해 최악 케이스를 방지하는 것이 가능합니다.
5. 퀵 정렬과 병합 정렬의 차이
분할 정복 공통 흐름
분할 정복 알고리즘은 공통적으로 다음 구조를 따릅니다:
- 문제를 더 작은 문제로 분할
- 작은 문제들을 각각 해결
- 해결한 결과들을 다시 병합
a. 퀵 정렬의 분할 정복
- 분할 : 피벗 기준으로 좌/우 그룹 나누기
- 정복 : 양쪽 그룹에 퀵 정렬 재귀 수행
- 병합 : 병합 생략 (스왑 기반 정렬이므로)
b. 병합 정렬의 분할 정복
- 분할 : 중간 인덱스로 리스트 나누기
- 정복 : 양쪽 각각 병합 정렬 재귀 수행
- 병합 : 정렬된 두 리스트를 병합하여 하나로 만들기
6. 마무리
- 퀵 정렬은 분할 정복 방식을 사용하는 매우 빠른 정렬 알고리즘입니다.
- 피벗을 기준으로 좌우를 분할하고 재귀적으로 정렬합니다.
- 평균적으로 O(n log n)의 성능을 보이며, 실무에서도 널리 사용됩니다.
- 다만 최악의 경우 성능이 급격히 나빠질 수 있으므로 피벗 선택 전략이 중요합니다.
- 불안정 정렬이므로 상황에 따라 병합 정렬 등과 비교하여 선택해야 합니다.
함께 보면 좋은 자료
외부 사이트 :
블로그 글 :
[정렬] 꼭 알아야 할 7가지 정렬 알고리즘 비교 정리
정렬(Sorting) 정렬(Sorting)은 데이터를 일정한 순서(오름차순, 내림차순 등)로 재배치하는 알고리즘입니다. 정렬은 탐색, 최적화, 통계 처리 등 거의 모든 컴퓨터 과학 문제의 전처리 단계로 중요하
dachaes-devlogs.tistory.com
[병합 정렬] 나누고 정렬하고 다시 합치는 정렬의 정석
병합 정렬(Merge Sort) 병합 정렬(Merge Sort)은 분할 정복(Divide and Conquer) 전략을 사용하는 대표적인 정렬 알고리즘입니다. 데이터를 더 이상 나눌 수 없을 만큼 작게 쪼갠 뒤, 나눈 조각들을 정렬하면
dachaes-devlogs.tistory.com
'코딩테스트 > 알고리즘' 카테고리의 다른 글
[병합 정렬] 나누고 정렬하고 다시 합치는 정렬 알고리즘 (0) | 2025.05.05 |
---|---|
[삽입 정렬] 카드를 정렬하듯, 데이터를 끼워 넣는 알고리즘 (0) | 2025.05.02 |
[선택 정렬] 가장 작은 값을 골라 정렬하는 알고리즘 (0) | 2025.05.02 |
[버블 정렬] 하나씩 비교하며 정렬하는 알고리즘 (0) | 2025.05.02 |
[정렬] 꼭 알아야 할 7가지 정렬 알고리즘 비교 정리 (0) | 2025.05.02 |