bubble sort
- 인접한 두 요소를 검사하여 정렬하는 가장 간단한 정렬 알고리즘
def bubble_sort(arr):
n = len(arr)
for i in range(n):
for j in range(0, n-i-1):
if arr[j] > arr[j+1]:
arr[j], arr[j+1] = arr[j+1], arr[j]
return arr
insertion sort
- 각 반복에서 하나의 요소를 취하여, 그 요소가 올바른 위치에 삽입될 때까지 이미 정렬된 배열 부분과 비교하여 재배치
def insertion_sort(arr):
for i in range(1, len(arr)):
key = arr[i]
j = i-1
while j >=0 and key < arr[j]:
arr[j+1] = arr[j]
j -= 1
arr[j+1] = key
return arr
- 최선의 경우 시간복잡도 O(n)
- 이미 정렬된 배열에서는 각 요소가 정확히 한 번씩만 비교됨
- 최악의 경우 시간복잡도 O(n2)
- 대부분의 경우나 배열이 역순으로 정렬되어 있는 경우, 첫 번째 요소를 제외한 모든 요소는 자신보다 앞에 있는 모든 요소와 비교됨
- 예를 들어, 두 번째 요소는 최대 1번, 세 번째 요소는 최대 2번, ..., n번째 요소는 최대 n−1번 비교 => 등차수열의 합 공식에 따라 2n(n−1) => 빅오표기법에서는 가장 높은 차수의 항만을 고려하며, 계수는 무시함 (같은 n2로 표시되더라도 bubble sort보다는 빠름)
merge sort
- 리스트를 반으로 나누어 각각을 재귀적으로 정렬한 후, 두 개의 정렬된 리스트를 하나의 정렬된 리스트로 병합
def merge_sort(arr):
if len(arr) > 1:
mid = len(arr) // 2
L = arr[:mid]
R = arr[mid:]
merge_sort(L)
merge_sort(R)
i = j = k = 0
while i < len(L) and j < len(R):
if L[i] < R[j]:
arr[k] = L[i]
i += 1
else:
arr[k] = R[j]
j += 1
k += 1
while i < len(L):
arr[k] = L[i]
i += 1
k += 1
while j < len(R):
arr[k] = R[j]
j += 1
k += 1
return arr
- 시간복잡도는 언제나 O(nlogn)
- 병합과정은 각 요소를 한 번씩만 처리하기 때문에 O(n)
- 분할과정에서 로그 수만큼의 단계가 생성되므로 O(logn)
- 병합이 여러 분할 단계에서 반복되므로 전체 시간복잡도는 O(nlogn)
quick sort
- 배열의 중간을 pivot이라고 정함
- 피벗보다 작은 요소들은 피벗의 왼쪽, 큰 요소들은 오른쪽으로 이동
- 왼쪽과 오른쪽 리스트를 동일한 방식으로 재귀적으로 정렬
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)
- 최선의 경우 시간복잡도 O(nlogn)
- 피벗이 매번 정확히 배열을 두 동등한 부분으로 나눌 때, 즉 매 분할마다 배열이 거의 반으로 나뉘는 경우
- 로그 레벨의 깊이를 가진 분할 트리를 생성하며, 각 레벨에서 n에 비례하는 작업을 수행
- 무작위로 선택된 데이터에 대해 퀵 정렬을 수행할 때에도 일반적으로 최선의 경우와 같다
- 최악의 경우 시간복잡도 O(n2)
- 피벗 선택이 매번 최소값이나 최대값으로 이루어져 배열이 한 쪽으로 치우쳐 분할될 때, 즉 한 부분은 n−1 요소를 가지고 다른 한 부분은 0 요소를 가질 때
- 분할 트리의 깊이가 n이 되며, 각 레벨에서 n에 비례하는 작업을 수행
- 데이터 분할 최적화: 퀵 정렬의 성능을 개선하기 위해, 작은 배열에 대해서는 삽입 정렬과 같은 더 간단한 정렬 방법을 사용하는 하이브리드 접근 방식 적용
- 재귀 깊이 제한: 최악의 경우를 방지하기 위해 재귀 호출의 깊이에 제한을 두거나, 너무 깊어지는 경우 다른 정렬 방법으로 전환