Quick Sort는 분할 정복(divide and conquer)방법을 통해 주어진 배열을 정렬하는 방법이다.
from typing import MutableSequence
def qsort(a,left,right):
pl = left
pr = right
x = a[(left+right)//2]
while pl <= pr:
while a[pl]<x : pl += 1
while a[pr]>x : pr -= 1
if pl <= pr:
a[pl],a[pr] = a[pr],a[pl]
pl += 1
pr -= 1
if left<pr:qsort(a,left,pr)
if pl<right:qsort(a,pl,right)
def quick_sort(n):
qsort(n,0,len(n)-1)
if __name__=='__main__':
print('퀵 정렬을 수행합니다.')
num = int(input('원소 수를 입력하세요. : '))
x = [None]*num #원소 수가 num인 배열을 생성
for i in range(num):
x[i] = int(input(f'x[{i}]'))
quick_sort(x)
print('오름차순으로 정렬했습니다')
for i in range(num):
print(f'x[{i}] = {x[i]}')
비교횟수 (log2n)
n이 2의 거듭제곱이라고 가정(n=2^k)했을 때, n=2^3의 경우, 2^3->2^2->2^1->2^0 순으로 줄어들어 호출의 깊이가 3임을 알 수 있다. 일반화를 통해 n=2^k인 경우 k = log2n으로 표현할 수 있다.
각 순환 호출 단계의 비교 연산(n)
각 순환 호출에서는 전체 리스트의 대부분의 레코드를 비교해야 하므로 평균 n번 정도의 비교가 이루어진다.
따라서, 최선의 시간복잡도는 순환 호출의 깊이 * 각 순환 호출 단계의 비교 연산 = nlog2n이 된다.
최악의 경우는 정렬하고자 하는 배열이 오름차순 정렬되어 있거나, 내림차순 정렬되어 있는 경우다.
순환 호출 단계에서 평균 n번의 비교가 이루어 질 때,
순환 호출의 깊이 * 각 순환 호출 단계의 비교연산 = n^2이다.
주어진 배열 안에서 교환(swap)을 통해, 정렬이 수행되므로 O(n)이다.
- 불필요한 데이터의 이동을 줄이고 먼 거리의 데이터를 교환할 뿐만 아니라, 한번 결정된 피벗들이 추후 연산에서 제외되는 특성 때문에, 시간 복잡도가 O(nlog2n)을 가지는 다른 정렬 알고리즘(heap,merge)과 비교했을 때, 속도가 가장 빠르다
- 정렬하고자 하는 배열 안에서 교환하는 방식으므로, 다른 메모리 공간을 필요로 하지 않는다.
정렬된 배열과 역순 배열일 때, 퀵 정렬의 속도가 느려지는 것은 pivot으로 정해진 위치의 값을 택하기 때문에, 가장 큰 값이나 가장 작은 값이 pivot이 되기 때문이다.