퀵 정렬(Quick Sort)
정렬 알고리즘 중 하나인 퀵 정렬(Quick Sort) 은 분할 정복(Divide and Conquer) 전략을 사용하여 데이터를 정렬하는 효율적인 방법입니다. 이번 글에서는 퀵 정렬의 개념과 동작 방식, 그리고 Python을 활용한 구현 방법까지 자세히 살펴보겠습니다.
1. 퀵 정렬의 개념
퀵 정렬은 다음과 같은 방식으로 데이터를 정렬합니다.
- 기준값(Pivot) 설정
리스트에서 하나의 원소를 선택하여 피벗(Pivot) 으로 정합니다. - 분할(Partitioning)
피벗보다 작은 값은 왼쪽, 큰 값은 오른쪽으로 분할합니다. - 재귀 호출(Recursion)
분할된 왼쪽과 오른쪽 리스트를 각각 다시 퀵 정렬을 수행합니다.
이를 반복하면 리스트가 정렬됩니다.
퀵 정렬은 평균적으로 O(n log n) 의 시간 복잡도를 가지며, 빠르고 효율적인 정렬 방법입니다.
2. 퀵 정렬의 동작 방식
예를 들어, 리스트 [4, 3, 1, 2, 5] 를 퀵 정렬하는 과정을 단계별로 살펴보겠습니다.
- 리스트에서 첫 번째 원소 4 를 피벗으로 선택합니다.
- 피벗 4 보다 작은 값 [3, 1, 2] 는 왼쪽에, 큰 값 [5] 는 오른쪽에 위치하도록 정렬합니다.
- 왼쪽 [3, 1, 2] 와 오른쪽 [5] 리스트에 대해 다시 퀵 정렬을 수행합니다.
- 이를 반복하면 최종적으로 [1, 2, 3, 4, 5] 로 정렬됩니다.
3. 퀵 정렬의 구현 (Python 코드)
이제 퀵 정렬을 Python 코드로 구현해 보겠습니다.
3.1. 퀵 정렬 함수
def quick_sort(seq: list) -> None:
"""퀵 정렬"""
def partition(start: int, end: int) -> int:
"""피벗을 기준으로 리스트를 분할하는 함수"""
i = start + 1
j = end
pivot = start # 피벗을 리스트의 첫 번째 원소로 설정
while True:
# 피벗보다 큰 값을 찾을 때까지 오른쪽으로 이동
while i <= end and seq[i] <= seq[pivot]:
i += 1
# 피벗보다 작은 값을 찾을 때까지 왼쪽으로 이동
while j >= start + 1 and seq[j] > seq[pivot]:
j -= 1
if i > j: # 인덱스가 엇갈렸다면 피벗과 j 위치의 값을 교환
seq[j], seq[pivot] = seq[pivot], seq[j]
break
# i와 j 위치의 값을 교환
seq[i], seq[j] = seq[j], seq[i]
return j # 피벗의 새로운 위치 반환
def sort(start: int, end: int) -> None:
"""재귀적으로 정렬을 수행하는 함수"""
if start < end:
pivot = partition(start, end) # 피벗 위치 찾기
sort(start, pivot - 1) # 피벗 왼쪽 정렬
sort(pivot + 1, end) # 피벗 오른쪽 정렬
sort(0, len(seq) - 1) # 정렬 수행
3.2. 퀵 정렬 실행 예제
arr = [4, 3, 1, 2, 5]
quick_sort(arr)
print(arr)
출력 결과
이제 위 코드를 단계별로 설명하겠습니다.
- quick_sort 함수는 sort 함수와 partition 함수를 포함하고 있습니다.
- partition 함수는 리스트를 피벗을 기준으로 두 개의 그룹으로 나누는 역할을 합니다.
- sort 함수는 재귀적으로 partition을 호출하여 정렬을 수행합니다.
- 최종적으로 정렬이 완료되면, 원래 리스트가 오름차순으로 정렬됩니다.
4. 퀵 정렬의 시간 복잡도 분석
퀵 정렬의 시간 복잡도는 다음과 같습니다.|
평균 시간 복잡도: O(n log n)
최악의 경우 (이미 정렬된 리스트에서 피벗을 잘못 선택할 경우): O(n^2)
최선의 경우 (항상 리스트를 절반씩 나누는 경우): O(n log n)
퀵 정렬은 평균적으로 매우 빠른 정렬 알고리즘 이지만, 최악의 경우 성능이 저하될 수 있습니다. 이를 방지하기 위해 무작위 피벗 선택(Randomized Quick Sort) 등의 방법을 활용할 수 있습니다. 다음 게시글에서는 이러한 무작위 피벗 선택 방법을 활용해 보고 비교해보겠습니다.
5. 장점과 단점
장점
평균적으로 O(n log n) 의 시간 복잡도를 가지며, 매우 빠릅니다.
추가적인 메모리 공간이 거의 필요하지 않아 메모리 효율적 입니다.
대부분의 실제 데이터에 대해 매우 우수한 성능 을 발휘합니다.
단점
최악의 경우 시간 복잡도가 O(n²) 이 될 수 있습니다.
안정 정렬이 아니므로, 같은 값의 원소 순서가 바뀔 수 있습니다.
분할 과정에서 피벗을 잘못 선택하면 성능이 저하될 수 있습니다.
결론
퀵 정렬은 빠르고 효율적인 정렬 알고리즘 으로, 실제 정렬 라이브러리에서도 많이 사용되는 방식 입니다. 평균적인 성능이 뛰어나며, 대량의 데이터를 정렬할 때 매우 효과적입니다. 다만, 피벗을 잘 선택해야 최악의 경우를 방지할 수 있다는 점도 고려해야 합니다.
이번 게시글에서는 재귀를 사용하여 퀵 정렬을 구현 하였습니다.
다음 게시글에서는 무작위 피벗 선택(Randomized Quick Sort) 등의 최적화 방법에 대해 자세히 다뤄보겠습니다.
목차 인덱싱은 결론만을 위한 사람을 위해 번호를 붙였으니 바쁘신 분들은 코드와 결론 부분만 확인하셔도 좋습니다!
'Data Structure' 카테고리의 다른 글
정렬 | 병합정렬 | 안정적이고 강력한 정렬 알고리즘 (0) | 2025.02.13 |
---|---|
정렬 | 삽입정렬 | 특이 case 에 적합한 정렬 (3) | 2025.01.23 |
정렬 | 삽입정렬 | 특이 case에 적합한 정렬 (0) | 2025.01.23 |
누구나 구현 할 수 있는 알고리즘 II (0) | 2025.01.20 |
정렬 | 선택정렬 | 누구나 구현할 수 있는 정렬 알고리즘 (0) | 2025.01.20 |