퀵 정렬(Quick Sort)
분할 정복(Divide and Conquer) 알고리즘의 일종, 평균적으로 매우 빠른
정렬 알고리즘
단계
분할(Divide)
배열에서 피벗(pivot) 요소를 선택함
피벗을 기준으로 배열을 두 부분으로 나눔, 피벗보다 작은 요소들은
피벗의 왼쪽 부분에 피벗보다 큰 요소들은 피벗의 오른쪽 부분에 위
치함
정복(Conquer)
분할 된 2 부분에 대해 재귀적으로 퀵 정렬을 수행함
결합(Combine)
부분 배열들이 모두 정렬되면 이들을 결합하여 전체 배열이 정렬됨
특징
평균 시간 복잡도 O(n log n)
최악 시간 복잡도 O(n*2) 피벗이 항상 최솟값 또는 최댓값으로 선택될 경
우
공간 복잡도 O(log n) 재귀 호출 스택 사용
불안정 정렬 동일한 값의 원소들의 상대적인 위치가 변경될 수 있음
과정
피벗 선택
피벗을 배열의 첫번째 요소, 마지막 요소, 중간 요소, 또는 무작위 요
소로 선택할 수 있음
분할
피벗을 기준으로 배열을 두 부분을 나눔
분할 과정에서 피벗보다 작은 요소들은 피벗의 왼쪽에, 피벗보다 큰
요소들은 피벗의 오른쪽에 위치
재귀호출
분할된 두 부분 배열에 대해 재귀적으로 퀵 정렬을 수행
def quick_sort(arr):
if len(arr) <= 1:
return arr
pivot = arr[len(arr) // 2]
# 피벗 선택 (중간
요소)
left = [x for x in arr if x < pivot]
# 피
벗보다 작은 요소들
middle = [x for x in arr if x == pivot]
#
피벗과 같은 요소들
right = [x for x in arr if x > pivot]
#
피벗보다 큰 요소들
return quick_sort(left) + middle +
quick_sort(right)
# 사용 예시
arr = [38, 27, 43, 3, 9, 82, 10]
sorted_arr = quick_sort(arr)
print(sorted_arr)
# 출력: [3, 9, 10, 27, 38,
43, 82]
[new_element for element in iterable if
condition]
new_element : 새로 생성될 리스트의 요소 정의
element : 반복(iterable)의 각 요소를 나타냄
iterable : 리스트, 문자열, 튜플 등 반복 가능한 객체를 나타냄
if condition : (선택 사항) 각 요소에 대해 조건을 검사하여, 조건을 만족
하는 요소만 리스트에 포함시킴
arr = [38, 27, 43, 3, 9, 82, 10]
pivot = 27
left = [x for x in arr if x < pivot]
# 피벗
보다 작은 요소들 # left = [3, 9, 10]
middle = [x for x in arr if x == pivot]
#
피벗과 같은 요소들 # middle = [27]
right = [x for x in arr if x > pivot]
벗보다 큰 요소들 # right = [38, 43, 82]
inplace(추가 메모리 없이)로 퀵정렬 구현하기
def partition(array, low, high):
# 선택된 피벗(pivot)은 배열의 마지막 요소
pivot = array[high]
# i는 낮은 인덱스보다 하나 적게 초기화
i = low - 1
# low부터 high - 1까지 반복
for j in range(low, high):
# 현재 요소가 피벗보다 작으면
if array[j] < pivot:
# i를 증가시키고
i += 1
# array[i]와 array[j]를 교환
# 피
array[i], array[j] = array[j],
array[i]
# 마지막으로 array[i + 1]과 array[high]를 교환하여
피벗을 올바른 위치에 놓음
array[i + 1], array[high] = array[high],
array[i + 1]
# 피벗의 위치를 반환
return i + 1
def quick_sort(array, low, high):
# low가 high보다 작을 때만 정렬 진행
if low < high:
# partition 함수를 호출하여 피벗의 위치를 얻음
pi = partition(array, low, high)
# 피벗의 왼쪽 부분 배열을 재귀적으로 정렬
quick_sort(array, low, pi - 1)
# 피벗의 오른쪽 부분 배열을 재귀적으로 정렬
quick_sort(array, pi + 1, high)
# 예제 사용법
if __name__ == "__main__":
# 정렬할 배열 정의
arr = [10, 7, 8, 9, 1, 5]
# 배열의 길이를 구함
n = len(arr)
# quick_sort 함수 호출
quick_sort(arr, 0, n - 1)
# 정렬된 배열 출력
print("Sorted array is:", arr)
def quick_sort(S):
"""퀵 정렬 알고리즘을 사용하여 큐 S의 요소를 정렬"""
n = len(S)
# 큐 S의 길이
if n < 2:
# 리스트 길이가 2보다 작으면
return
# 리스트는 이미 정렬된 상태이므로 반환
# 나누기
p = S.first()
# 첫 번째 요소를 피벗으로 선택
L = LinkedQueue()
# 피벗보다 작은 요소들을 저장할 큐
E = LinkedQueue()
# 피벗과 같은 요소들을 저장할 큐
G = LinkedQueue()
# 피벗보다 큰 요소들을 저장할 큐
# S를 L, E, G로 나누기
while not S.is_empty():
# 큐 S가 비어 있지 않으면
if S.first() < p:
L.enqueue(S.dequeue())
# 피벗보다 작은 요소
를 L에 추가
elif p < S.first():
G.enqueue(S.dequeue())
# 피벗보다 큰 요소를
G에 추가
else:
E.enqueue(S.dequeue())
# 피벗과 같은 요소를
E에 추가
# 정복 단계 (재귀 호출을 통해 정렬)
quick_sort(L)
# 피벗보다 작은 요소들을 정렬
quick_sort(G)
# 피벗보다 큰 요소들을 정렬
# 결과 병합
while not L.is_empty():
S.enqueue(L.dequeue())
# L의 요소를 S에 추가
while not E.is_empty():
S.enqueue(E.dequeue())
# E의 요소를 S에 추가
while not G.is_empty():
S.enqueue(G.dequeue())
# G의 요소를 S에 추가
def inplace_quick_sort(S, a, b):
"""퀵 정렬 알고리즘을 사용하여 리스트 S의 인덱스 a부터 b까지
를 정렬"""
if a >= b:
# 정렬 범위가 1 이하이면
return
# 이미 정렬된 상태이므로 반환
pivot = S[b]
left = a
# 피벗을 범위의 마지막 요소로 선택
# 왼쪽 인덱스 설정
right = b - 1
# 오른쪽 인덱스 설정
while left <= right:
# 왼쪽 인덱스가 오른쪽 인덱스보다
작거나 같을 때까지 반복
# 피벗보다 큰 요소를 만날 때까지 왼쪽 인덱스를 오른쪽으로
이동
while left <= right and S[left] < pivot:
left += 1
# 피벗보다 작은 요소를 만날 때까지 오른쪽 인덱스를 왼쪽으
로 이동
while left <= right and pivot < S[right]:
right -= 1
if left <= right:
# 인덱스가 교차되지 않았다면
S[left], S[right] = S[right], S[left]
# 두 요소를 교환
left, right = left + 1, right - 1
축소
# 피벗을 제 위치에 놓기
S[left], S[b] = S[b], S[left]
# 범위
# 재귀 호출로 두 부분을 정렬
inplace_quick_sort(S, a, left - 1)
inplace_quick_sort(S, left + 1, b)