분할과 정복 방법을 이용한다.
1. 주어진 크기가 n인 리스트 A를 크기가 n/2인 두 부분으로 나눈다.
2. 크기가 n/2인 두 부분을 재귀적으로(recursively) 정렬한다.
3. 정렬된 두 부분을 하나로 병합한다.
Algorithm mergeSort(A, left, right)
if (left < right)
mid = (left + right) / 2
mergeSort(A, left, mid)
mergeSort(A, mid+1, right)
merge(A, left, mid, right)
array = [5, 3, 2, 1, 6, 8, 7, 4]
def merge_sort(array):
# 이 곳을 채워보세요!
n = len(array)
mid = (0 + n)//2
if n == 1:
return array
left_list = merge_sort(array[:mid])
right_list = merge_sort(array[mid:])
array = merge(left_list, right_list)
return array
def merge(array1, array2): # 최악의 경우 array1과 array2의 길이의 합 만큼 시간복잡도가 걸림 : O(N)
result = []
array1_index = 0
array2_index = 0
while array1_index < len(array1) and array2_index < len(array2):
if array1[array1_index] < array2[array2_index]:
result.append(array1[array1_index])
array1_index += 1
else:
result.append(array2[array2_index])
array2_index += 1
if array1_index == len(array1):
while array2_index < len(array2):
result.append(array2[array2_index])
array2_index += 1
if array2_index == len(array2):
while array1_index < len(array1):
result.append(array1[array1_index])
array1_index += 1
return result
print(merge_sort(array)) # [1, 2, 3, 4, 5, 6, 7, 8] 가 되어야 합니다!
O(m+n)
T(n) : MergeSort(A, 0, n-1)을 수행하는데 최악의 경우 키 비교(기본연산) 수
T(n)은 O(n log n)
의 시간 복잡도를 갖는다.
최악의 경우든, 최선의 경우이든 모두 n log n
이유는n > 1 일 때,
T(n) >= 2T(n/2) + c2*n (c2 = 1/2)이므로 (모든 경우 절반만 비교하고 merge)
T(n)이 Ω(n log n)이기 때문임
따라서 입력의 형태와 관련없이 n lon n의 시간이 걸린다.
merge 할 때 O(n)의 추가적인 메모리가 필요하다. (비교연산에 들어감 - 공간적 측면)
재귀 호출시 O(log n)의 스택 메모리가 필요하다. (매 번 불러올 때마다 연산 결과를 저장해서 올라오므로??)
(: 재귀를 이용하지 않고 반복을 이용하면 개선 가능 -> 중첩 반복문 이용)
def seq_merge_sort(arr):
rght = 0; wid = 0; rend = 0; left = 0
k = 1
num = len(arr)
temp = [0] * num
while(k < num):
left = 0
while(left + k < num):
rght = left + k
rend = rght + k
if(rend > num):
rend = num
m = left; i = left; j = rght
while(i < rght and j < rend):
if (arr[i] <= arr[j]):
temp[m] = arr[i]
i += 1
else:
temp[m] = arr[j]
j += 1
m += 1
while(i < rght):
temp[m] = arr[i]
i += 1; m += 1
while(j < rend):
temp[m] = arr[j]
j += 1; m += 1
m = left
while(m < rend):
arr[m] = temp[m]
m += 1
left += k * 2
k *= 2
return arr
1) 리스트(배열) A에서 pivot을 선택한다.
2) 리스트(배열) A를 다음과 같이 분할(partition)한다.
pivot보다 작은 수는 pivot의 왼쪽으로, pivot보다 큰 수는 pivot의 오른쪽으로 이동하여 재배열한다.
3) 분할된 두 리스트 A1,A2를 재귀적으로 정렬한다(각각 Quick sort). 즉 같은 방법(단계1,2를 적용한다.)
퀵정렬 알고리즘
Algorithm quickSort(A, left, right)
if (left < right) {
pivotPoint = partition(A, left, right) // pivotPoint는 분할 후 피봇의 위치
quickSort(A, left, pivotPoint -1)
quickSort(A, pivotPoint+1, right)
리스트(배열) A의 정렬 : main 함수에서 quickSort(A, 0, n-1)을 호출
특징
- 첫 번째 원소를 피봇으로 한다.
- 다음 원소 i는 pivot보다 큰 수를 만날 때까지 오른쪽으로 이동한다.
- 맨 마지막 원소 j는 pivot보다 작거나 같은 수를 만날 때까지 왼쪽으로 이동한다.
- 만약 i < j라면 A[i]와 A[j]를 바꾼다.
Algorithm partition(A, left, right)
i = left + 1, j = right
pivot = A[left]
while (i <= j)
while(i <= right and A[i] < pivot)
i += 1
while(j >= left and A[j] >= pivot)
j -= 1
if (i <= j)
A[i] 와 A[j]를 교환
i를 1 증가, j를 1 감소
A[left]와 A[j]를 교환
return j
i > j 라면 멈춘다.
의 의미i>j
를 기점으로 하여 완전히 나뉘었다는 말이다.A = [4, 1, 3, 8, 6, 2, 5, 7, 10]
def partition(A, left, right):
i = left + 1
j = right
pivot = A[left]
while(i<=j):
while(i<=right and A[i] < pivot):
i += 1
while(j>=left and A[j] >= pivot):
j -= 1
if (i<=j):
A[i], A[j] = A[j], A[i]
A[left], A[j] = A[j], A[left]
return j
def quick_sort(numbers, left, right):
if left <= right:
pivot_point = partition(numbers, left, right)
quick_sort(numbers, left, pivot_point - 1) # left side
quick_sort(numbers, pivot_point + 1, right) # right side
quick_sort(A, 0, len(A)-1)
Algorithm partition(A, left, right)
pivot = a[right]
pivotPoint = left - 1
for i = left to right - 1
if (A[i] <= pivot)
pivotPoint를 1 증가
A[i]와 A[pivotPoint]를 교환
A[pivotPoint + 1]과 A[right]를 교환
return pivotPoint + 1
A(n) : O(n log n)
O(n log n)
)O(N^2)
은 변함이 없다.def partition(numbers, left, right):
pivot = random.choice(numbers) # 랜덤으로 pivot 선택
n = len(numbers)
pivot_index = numbers.index(pivot)
numbers.pop(pivot_index)
left_side = []
right_side = []
for i in range(n-2): # 0 ~ n-2 /// n-1 은 pivot
if (numbers[i] <= pivot): # pivot보다 작으면..
left_side.append(numbers[i])
else:
right_side.append(numbers[i])
left_side.append(pivot) # 왼쪽 편에 pivot 넣고~
left_side.extend(right_side) # 나머지 오른쪽 편들 다 밀어 넣기
return left_side.index(pivot)