버블/선택/삽입 정렬은 느리다. 마치 거북이 같다. 퀵 정렬은 평균적으로 빠르지만 최악의 경우 느려진다. 음... 토북이라고 하자.
| 정렬 | 설명 | 시간복잡도 | 공간복잡도 | 안정성 |
|---|---|---|---|---|
| 버블 정렬 | 이웃한 두 원소를 비교 및 교환 | O | ||
| 선택 정렬 | 제일 작은 원소를 맨 앞 원소와 교환 | X | ||
| 삽입 정렬 | 원소를 이미 정렬된 원소들 속에 삽입 | 거의 정렬된 배열: 근접 | O | |
| 퀵 정렬 | 피벗 기준으로 더 작은 / 큰 값으로 나누며 정렬 | 평균 최악 | 평균 최악 | X |

def bubble_sort(a):
N = len(a)
for i in range(N - 1):
# 앞쪽 i개 원소까지는 정렬됐고, a[i]부터 정렬되지 않음
for j in range(N - 1, i, -1):
# 앞뒤 원소 값을 비교하여, 앞쪽 값이 더 크면 교환
if a[j - 1] > a[j]:
a[j - 1], a[j] = a[j], a[j - 1]
arr = [6, 4, 3, 7, 1, 9, 8]
bubble_sort(arr)
print(arr) # [1, 3, 4, 6, 7, 8, 9]
a[min] 선택a[min]과 아직 정렬되지 않은 부분의 맨 앞 원소를 교환
def selection_sort(a):
N = len(a)
for i in range(N - 1):
# 정렬되지 않은 부분에서 가장 작은 원소의 인덱스
min_idx = i
for j in range(i + 1, N):
if a[j] < a[min_idx]:
min_idx = j
# 정렬되지 않은 부분에서, 맨 앞 원소와 최솟값 원소 교환
a[i], a[min_idx] = a[min_idx], a[i]
arr = [6, 4, 8, 3, 1, 9, 7]
selection_sort(arr)
print(arr) # [1, 3, 4, 6, 7, 8, 9]
시간 복잡도: ->
공간 복잡도: 내부에서 새로운 배열을 만들지 않고, 기존 배열의 값만 바꿈 ->

서로 이웃하지 않은 원소를 교환하므로, 안정적이지 않음
3B가 3A보다 앞에 정렬된 것을 확인할 수 있음
def insertion_sort(a):
N = len(a)
for i in range(1, N):
j = i
temp = a[i]
# 1. 배열의 왼쪽 끝에 도달할 때까지
# 2. 이웃한 왼쪽 원소가 temp보다 작거나 같을 때까지
# 아래 과정을 반복
while j > 0 and a[j - 1] > temp:
a[j] = a[j - 1] # 이웃한 왼쪽 원소를 현 위치에 대입
j -= 1
a[j] = temp # 멈춘 위치에 temp를 대입
arr = [6, 4, 1, 7, 3, 9, 8]
insertion_sort(arr)
print(arr) # [1, 3, 4, 6, 7, 8, 9]

start, 끝 인덱스를 end로 둠l를 start 위치에, r를 end 위치에 둠pivot 값을 설정함 (여기선 중앙값)a[l] >= pivot인 원소를 찾을 때까지 l을 오른쪽으로 이동a[r] <= pivot인 원소를 찾을 때까지 r를 왼쪽으로 이동l와 r가 모두 정지한 경우a[l]와 a[r] 값을 교환 (두 값이 동일해도 교환)l를 1 증가, r를 1 감소l와 r가 교차할 때까지 반복start <= r일 경우, a[0]부터 a[r]까지 왼쪽 부분배열도 재귀 호출로 정렬l <= end일 때, a[l] 부터 a[end]까지 오른쪽 부분배열도 재귀 호출로 정렬l > r + 1일 때 정지한 경우 a[r]와 a[l] 사이에 원소가 있을 때도 있음def quick_sort(a, start, end):
l = start # 왼쪽 인덱스
r = end # 오른쪽 인덱스
pivot = a[(l + r) // 2] # 피벗
print(f"a[{start}] ~ a[{end}]: {a[start:end + 1]}")
# 배열을 두 그룹으로 나누기
while l <= r:
while a[l] < pivot: l += 1
while a[r] > pivot: r -= 1
if l <= r:
a[l], a[r] = a[r], a[l]
# a[l] 혹은 a[r]과 pivot의 값이 동일할 때
# 탈출하기 위한 용도
l += 1
r -= 1
# 왼쪽 그룹을 나누기
if start < r:
quick_sort(a, start, r)
# 오른쪽 그룹을 나누기
if l < end:
quick_sort(a, l, end)
a = [5, 8, 4, 2, 6, 1, 3, 9, 7]
quick_sort(a, 0, len(a) - 1)
print(a) # [1, 2, 3, 4, 5, 6, 7, 8, 9]

a[end - 1]을 피벗으로 선택l의 시작위치를 start + 1로, r의 시작 위치를 end - 1로 이동한 후 스캔def sort3(a, idx1, idx2, idx3):
# 피벗 정하기 전 원소 3개 정렬
if a[idx1] > a[idx2]: a[idx1], a[idx2] = a[idx2], a[idx1]
if a[idx2] > a[idx3]: a[idx2], a[idx3] = a[idx3], a[idx2]
if a[idx1] > a[idx2]: a[idx1], a[idx2] = a[idx2], a[idx1]
return idx2 # 가운데 원소
def insertion_sort(a, start, end):
# 삽입 정렬
for i in range(start + 1, end + 1):
j = i
temp = a[i]
while j > start and a[j-1] > temp:
a[j] = a[j-1]
j -= 1
a[j] = temp
def quick_sort(a, start, end):
if (end - start + 1) <= 9:
insertion_sort(a, start, end)
else:
l = start
r = end
# 첫, 가운데, 끝 원소를 정렬
mid = sort3(a, l, (l + r) // 2, r)
pivot = a[mid] # 가운데 원소의 값을 피벗으로 설정
# 가운데 원소와, 끝에서 2번째 원소 교환
a[mid], a[r - 1] = a[r - 1], a[mid]
# left를 우측 1칸, right를 좌측 2칸 이동
l += 1
r -= 2
# 이후 과정은 기존 알고리즘과 동일
while l <= r:
while a[l] < pivot: l += 1
while a[r] > pivot: r -= 1
if l <= r:
a[l], a[r] = a[r], a[l]
l += 1
r -= 1
if start < r: quick_sort(a, start, r)
if l < end: quick_sort(a, l, end)
a = [5, 8, 4, 2, 6, 1, 3, 9, 7, 0, 3, 5]
quick_sort(a, 0, len(a) - 1)
print(a)
이걸로 학습 날로먹어야겠다ㅋㅋ