기준 데이터(피벗, pivot)를 설정하여, 그 기준보다 큰 데이터와 작은 데이터를 구분하는 '분할(patition)' 알고리즘을, 재귀적으로 반복하는 정렬 알고리즘.
분할정복 알고리즘에 맞게 3단계로 나누어 설명하면 아래와 같다.
Divide (Partition)
i
와 j
를 맨 왼쪽에서부터 시작, 피벗은 맨 오른쪽에 위치j
를 증가시키면서j
의 요소 값이 피벗보다 작을 경우, swap(A[i], A[j])
i += 1
j
가 맨 오른쪽에 도달하면, 피벗과 i의 요소 값을 swapi
는 (맨 왼쪽 + 1)에서, j
는 맨 오른쪽에서 시작. 피벗은 맨 왼쪽에 위치i
에서부터는 피벗보다 큰 값을, j
에서부터는 피벗보다 작은 값을 선택하여 두 값을 swap.Conquer
Combine
# 분할 함수 정의 (by Lomuto)
def partition(A, low, high):
pivot_idx = low # 피벗의 인덱스를 임의의 값으로 설정 (현재는 제일 왼쪽 값으로 설정)
pivot = A[pivot_idx]
A[pivot_idx], A[high] = A[high], A[pivot_idx] # 비교를 위해 피벗을 가장 오른쪽 값과 swap
i = low
for j in range(low, high):
if A[j] < pivot: # 인덱스 j는 피벗보다 작은 값을 찾아서, A[i]와 swap
A[i], A[j] = A[j], A[i]
i += 1
pivot_idx = i # 반복문을 마친 배열에서 i는 피벗의 인덱스가 되어야 함.
A[pivot_idx], A[high] = A[high], A[pivot_idx]
return pivot_idx
# 퀵 정렬 함수 정의
def quick_sort(A, low, high):
if low < high:
pivot_idx = partition(A, low, high) # 분할 함수를 진행하여 피벗을 기준으로 분할을 진행
quick_sort(A, low, pivot_idx - 1) # 분할의 왼쪽 부분에 대해서 재귀
quick_sort(A, pivot_idx + 1, high) # 분할의 오른쪽에 대해서 재귀
# 테스트 코드
if __name__ == "__main__":
A = [5, 6, 4, 0, 2, 1, 7, 9, 3, 8]
low, high = 0, len(A)-1
print(f"BEFORE QUICK_SORT : {A}")
quick_sort(A, low, high)
print(f"AFTER QUICK_SORT : {A}")
BEFORE QUICK_SORT : [5, 6, 4, 0, 2, 1, 7, 9, 3, 8]
AFTER QUICK_SORT : [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
""" 테스트 코드의 분할 함수 재현
| == pivot
* == swap
5 6 4 0 2 1 7 9 3 8 swap(A[pivot_idx], A[high])
|* *
8 6 4 0 2 1 7 9 3 5 swap(A[i], A[j])
* * |
i
j j j*
4 6 8 0 2 1 7 9 3 5 swap(A[i], A[j])
* * |
i
j j*
4 0 8 6 2 1 7 9 3 5 swap(A[i], A[j])
* * |
i
j*
4 0 2 6 8 1 7 9 3 5 swap(A[i], A[j])
* * |
i
j*
4 0 2 1 8 6 7 9 3 5 swap(A[i], A[j])
* * |
i
j j j*
4 0 2 1 3 6 7 9 8 5 swap(A[pivot_idx], A[high])
* |*
4 0 2 1 3 5 7 9 8 6 분할 함수 종료 -> 피벗의 인덱스 값 반환
|
(small) | (big)
- 위와 같이 분할(partition) 함수에 의해 5를 기준으로 분할 완료.
- 이것을 재귀로 반복하면 퀵 정렬이 완성
"""