- 분할 (Divide)
- 해결할 문제를 여러 개의 작은 부분으로 나눈다.
- 정복 (Conquer)
- 나눈 작은 문제를 각각 해결한다
- 통합 (Combine)
- (필요하다면) 해결된 답을 모은다
: 분할 정복은 하향식 접근법이다
- 반복 (Iterative) 알고리즘:
O(n)
- C의 거듭제곱 = 1에 거듭제곱할 값만큼 C를 곱하는 방식으로 연산 수행
- 분할 정복 기반 알고리즘: `O(logn)
- 거듭제곱을 반 씩 나누어서 곱해 나가는 방법으로 풀이
- 지수가 1이 되면 밑 수를 그대로 반환
- 시간 복잡도를
O(log n)
으로 줄일 수 있음!
- 시간 복잡도를
- 여러 개의 정렬된 자료의 집합을 병합하여 한 개의 정렬된 집합으로 만드는 방식
- 분할 정복 알고리즘 활용
- 자료를 최소 단위의 문제까지 나눈 후에 차례로 정렬하여 최종 결과를 얻어냄
- top-down 방식
- 시간 복잡도
- O(nlogn)
ex)
def merge(left, right):
result = [] # 두 개의 분할된 list를 병합하여 result를 만듦
while len(left) > 0 and len(right) > 0: # 양쪽 list에 원소가 남아있는 경우
# 두 sub list의 첫 원소들을 비교하여 작은 것부터 result에 추가함
if left[0] <= right[0]:
result.append(left.pop(0))
else:
result.append(right.pop(0))
if len(left) > 0: # 왼쪽 list에 원소가 남아있는 경우
result.extend(left)
if len(right) > 0: # 오른쪽 list에 원소가 남아있는 경우
result.extend(right)
return result
def merge_sort(m):
if len(m) <= 1: # 배열 크기가 0 이거나 1인 경우 바로 return
return m
# 1. Divide 부분
mid = len(m) // 2
left = m[:mid]
right = m[mid:]
# list의 크기가 1이 될 때까지 merge_sort() 재귀 호출
left = merge_sort(left)
right = merge_sort(right)
# 2. Conquer 부분 : 분할된 리스트들 병함
return merge(left, right)
- 주어진 배열을 두 개로 분할하고, 각각을 정렬한다
- 병합 정렬과 다른 점
- 병합 정렬은 그냥 두 부분으로 나누는 반면에, 퀵 정렬은 분할할 때, 기준 아이템(pivot item) 중심으로, 이보다 작은 것은 왼편, 큰 것은 오른편에 위치시킨다
- 각 부분 정렬이 끝난 후, 병합정렬을 "병합"이란 후처리 작업이 필요하나, 퀵 정렬은 필요로 하지 않는다.
- 시간 복잡도
- nlogn
- P(pivot) 값들 보다 큰 값은 오른쪽, 작은 값들은 왼쪽 집합에 위치하도록 한다
- Pivot을 두 집합의 가운데에 위치시킨다
- 왼쪽 끝 / 오른쪽 끝 / 임의의 세 개 값 중에 중간 값
ex)
def quick_sort (arr, left, right):
# pivot 위치 결정 (pivot을 기준으로 큰 값은 오른쪽, 작은 값은 왼쪽으로 구분)
# 왼쪽 부분집합 정렬
# 오른쪽 부분집합 정렬
if left > right:
return
# 피봇 구하기
pivot = lomuto_partition(arr, left, right)
pivot = hoare_partition(arr, left, right)
# 왼쪽 부분집합 정렬 실행
quick_sort(arr, left, pivot-1)
# 오르쪽 부분집합 정렬 실행
quick_sort(arr,pivot+1, right)
def lomuto_partition(arr, left, right):
# 맨 오른쪽 요소를 pivot으로 설정하고,
# i = left -1
# j = left
pivot = arr[right]
i = left -1
for j in range(left, right):
# arr[i] 가 pivot보다 작으면,
if arr[j] < pivot:
# i를 1 증가
# arr[j], arr[i] 위치 교환
i += 1
arr[i], arr[j] = arr[j], arr[i]
# i가 가리키는 위치가 pivot보다 작은 값의 마지막 인덱스
# i + 1 의 위치에 pivot을 두고 i+1 반환
arr[i+1], arr[right] = arr[right], arr[i+1]
return i+1
def hoare_partition(arr, left, right):
# i, j 설정하고, 큰값 & 작은 값 찾아서 위치 바꿔주기
i = left
j = right
pivot = arr[left]
# i가 j보다 작을 때 까지 게속해서 반복
while i <= j:
# pivot 보다 큰 값이 나올 때 까지 i 증가
while i<=j and arr[i] <= pivot:
i += 1
# pivot 보다 작은 값이 나올 때 까지 j 감소
while i <=j and arr[j] >= pivot:
j -= 1
# i가 j보다 작으면, 위치 교환
if i < j:
arr[i], arr[j] = arr[j], arr[i]
arr[left], arr[j] = arr[j], arr[left]
return j # pivot 위치인 j 반환
arr = [4,6,3,6,8,0,3,8,10]
quick_sort(arr, 0, len(arr)-1)
print(arr)
- 자료의 가운데에 있는 항목의 key 값과 비교하여 다음 검색의 위치를 결정하고 검색을 계속 진행하는 방법
- 목적 키를 찾을 때까지 이진 검색을 순환적으로 반복 수행함으로써 검색 범위를 반으로 줄여가면서 보다 빠르게 검색을 수행함
- 이진 검색을 하기 위해서는 자료가 정렬된 상태여야 한다.
-
자료의 중앙에 있는 원소를 고른다
-
중앙 원소의 값과 찾고자 하는 목표 값을 비교
-
목표 값이 중앙 원소의 값보다
- 작으면 자료의 왼쪽 반에 대해서 새로 검색을 수행
- 크다면 자료의 오른쪽 반에 대해서 새로 검색을 수행
-
찾고자 하는 값을 찾을 때까지 1~3 반복