퀵 정렬 알고리즘의 기본 개념
- 퀵 정렬은 불안정 정렬에 속하며, 다른 원소와의 비교만으로 정렬을 수행하는 비교 정렬에 속한다.
- 분할 정복 알고리즘의 하나로, 평균적으로 매우 빠른 수행 속도를 가진 정렬 방법이다.
- 병합 정렬과 다르게 퀵 정렬은 리스트를 비균등하게 분할한다.
- 분할 정복 방법
- 문제를 작은 2개의 문제로 분리하고 각각을 해결한 다음, 결과를 모아서 원래의 문제를 해결하는 전략
- 분할 정복 방법은 대게 순환 호출을 이용하여 구현한다.
- 알고리즘의 과정
- 리스트 안에 있는 한 원소를 선택한다. 이렇게 고른 원소를 피벗(pivot)이라고 한다.
- 피벗을 기준으로 피벗보다 작은 요소들은 모두 피벗의 왼쪽으로 옮겨지고 피벗보다 큰 요소들은 모두 피벗의 오른쪽으로 옮겨진다.
- 피벗을 제외한 왼쪽 리스트와 오른쪽 리스트를 다시 정렬한다.
- 분할된 부분 리스트에 대하여 재귀적 호출을 이용하여 정렬을 반복한다.
- 부분 리스트에서도 다시 피벗을 정하고 피벗을 기준으로 2개의 부분 리스트로 나누는 과정을 반복한다.
- 부분 리스트들이 더이상 분할이 불가능할 때까지 반복한다.
- 리스트의 크기가 0이나 1이 될 때까지 반복한다.
파이썬으로 코드 구현
코드 구현
def quicksort(data):
if len(data) <= 1:
return data
pivot = data[len(data) // 2] # 피벗값을 중앙값으로 잡기
left = [] # 피벗값보다 작은 데이터는 피벗의 왼쪽으로 정렬
for num in data:
if num < pivot:
left.append(num)
mid = [] # 피벗과 같은값이 있으면 mid 리스트로 추가
for num in data:
if num == pivot:
mid.append(num)
right =[] # 피벗값보다 큰 데이터는 피벗의 오른쪽으로 정렬
for num in data:
if num > pivot:
right.append(num)
return quicksort(left) + mid + quicksort(right) # 이 과정을 재귀 호출로 반복
알고리즘 테스트 1
data = [1, 9, 5, 8, 4, 6, 5, 3, 2, 7, 1, 9, 10]
print(quicksort(data))
결과
[1, 1, 2, 3, 4, 5, 5, 6, 7, 8, 9, 9, 10]
알고리즘 테스트 2
아래의 네가지 데이터를 넣고 테스트도 진행해 보았다.
- 정렬이 끝난 배열
- 역순으로 정렬된 배열
- 원소가 전부 같은 배열
- 랜덤으로 구성된 배열
import time
def quicksort(data):
if len(data) <= 1:
return data
pivot = data[len(data) // 2]
left = []
for num in data:
if num < pivot:
left.append(num)
mid = []
for num in data:
if num == pivot:
mid.append(num)
right =[]
for num in data:
if num > pivot:
right.append(num)
return quicksort(left) + mid + quicksort(right)
test1 = [1, 2, 3, 4, 5, 6, 7]
test2 = [7, 6, 5, 4, 3, 2, 1]
test3 = [1, 1, 1, 1, 1, 1, 1, 1]
test4 = [23, 15, 38, 94, 62, 123, 243, 234, 112]
start_time1 = time.time()
mergesort(test1)
end_time1 = time.time()
total_time1 = (end_time1 - start_time1) * 1000
start_time2 = time.time()
mergesort(test2)
end_time2 = time.time()
total_time2 = (end_time2 - start_time2) * 1000
start_time3 = time.time()
mergesort(test3)
end_time3 = time.time()
total_time3 = (end_time3 - start_time3) * 1000
start_time4 = time.time()
mergesort(test4)
end_time4 = time.time()
total_time4 = (end_time4 - start_time4) * 1000
print(f'{total_time1:.4f}ms')
print(f'{total_time2:.4f}ms')
print(f'{total_time3:.4f}ms')
print(f'{total_time4:.4f}ms')
결과
0.0532ms
0.0448ms
0.0448ms
0.0451ms
퀵 정렬의 장단점
장점
- 특정 상태(정렬된 상태)가 아닌 이상 평균 시간 복잡도는 O(nlogn)이며, 다른 O(nlogn) 알고리즘에 비해 대체적으로 속도가 매우 빠르다. 시간 복잡도가 O(nlogn) 정렬 알고리즘 중 분할 정복 방식인 병합정렬에 비해 2배 정도 빠르게 테스트 결과가 나왔다.
- 추가적인 별도의 메모리를 필요로 하지 않으며 재귀 호출 stack 프레임에 의한 공간 복잡도는 logn으로 메모리를 적게 소비함
단점
- 특정 조건(정렬된 상태)에서 성능이 떨어진다(O(n^2))
- 재귀를 사용하기 때문에 재귀를 사용하지 못하는 환경일 경우 그 구현이 매우 복잡해진다.
'자료구조, 알고리즘' 카테고리의 다른 글
| 탐색 알고리즘 (Search Algorithm) (0) | 2026.06.17 |
|---|---|
| 재귀 함수 (Recursive Function) (0) | 2026.06.14 |
| 병합 정렬 (Merge Sort) (0) | 2026.06.13 |
| 버블 정렬 vs 선택 정렬 vs 삽입 정렬 성능 테스트 코드 (0) | 2026.06.11 |
| 삽입 정렬 (Insertion Sort) (0) | 2026.06.10 |