일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
- 백준 17608번
- DFS(Depth First Search)
- 플로이드 워셜 알고리즘(Floyd-Warshall Algorithm)
- 위상 정렬(Topological Sort)
- 위상 정렬(Topology Sort)
- 알고리즘 개념
- 백준 18352번
- 백준 9012번
- 그래프(Graph)
- 백준 2812번
- 백준 10000번
- 분할 정복(Divide and Conquer)
- 백준 21606번
- 스택(Stack)
- 백준 2261번
- 이분 탐색(Binary Search)
- 이분 그래프(Bipartite Graph)
- DFS
- 그리디 알고리즘(Greedy Algorithm)
- 큐(Queue)
- 다익스트라 알고리즘(Dijkstra Algorithm)
- 트리(Tree)
- 백준 2504번
- DFS & BFS
- 백준 2493번
- BFS(Breadth First Search)
- 백준 1948번
- 동적 프로그래밍(Dynamic Programming)
- BFS
- 백준 1707번
- Today
- Total
Always Be Wise
정렬 알고리즘 - 힙 정렬(Heap Sort) 본문
완전 이진 트리를 기본으로 하는 힙(Heap) 자료구조를 기반으로한 정렬 알고리즘입니다.
힙은 큰 키(우선 순위)에 자주 액세스하거나 키(우선 순위) 중심으로 정렬된 시퀀스를 활용해야 할 때 유용한
자료구조입니다. 힙은 한 노드가 최대 두 개의 자식 노드를 가지면서, 마지막 레벨을 제외한 모든 레벨에서
노드들이 꽉 채워진 **완전 이진 트리(complete binary tree)**를 기본으로 합니다.
힙 속성(heap property)은 다음 두 가지입니다.
heap order property : 각 노드의 값은 자신의 자식노드가 가진 값보다 크거나 같다(최대 힙, Max heap).
각 노드의 값은 자신의 자식노드가 가진 값보다 작거나 같다(최소 힙, Min heap).
heap shape property : 모양은 완전 이진 트리이다. 즉 마지막 레벨의 모든 노드는 왼쪽에 쏠려 있다.
힙과 이진 탐색 트리(binary search tree) 모두 이진 트리라는 점에서 공통점을 가지만 노드값이 다소 다르게
구성돼 있는 점을 확인할 수 있습니다. 힙은 각 노드의 값이 자식노드보다 큰 반면, 이진 탐색 트리는 왼쪽 자식
노드가 제일 작고 부모노드가 그 다음 크며 오른쪽 자식노드가 가장 큰 값을 가집니다.
힙은 우선순위(키) 정렬에, 이진 탐색 트리는 탐색에 강점을 지닌 자료구조입니다.
힙은 1차원 배열(array)로도 표현이 가능합니다. 어떤 노드의 인덱스를 index, 왼쪽 자식노드의 인덱스를
left_index, 오른쪽 자식노드의 인덱스를 right _index로 선언하면 다음과 같은 관계를 지닙니다.
left_index = 2 * index + 1
right_index = 2 * index + 2
주어진 자료구조에서 힙 성질을 만족하도록 하는 연산을 heapify라고 합니다.
파이썬으로 구현하면 아래와 같습니다.
def heapify(unsorted, index, heap_size):
largest = index
left_index = 2 * index + 1
right_index = 2 * index + 2
if left_index < heap_size and unsorted[left_index] > unsorted[largest]:
largest = left_index
if right_index < heap_size and unsorted[right_index] > unsorted[largest]:
largest = right_index
if largest != index:
unsorted[largest], unsorted[index] = unsorted[index], unsorted[largest]
heapify(unsorted, largest, heap_size)
heapfify는 최악의 경우 루트노드에서 리프노드까지 값을 비교해야 하므로 트리의 최대 높이(logn)에
의존적입니다. 값을 비교하거나 바꾸는 연산은 O(1)이므로 결과적으로 O(logn)이 됩니다.
- 주어진 원소들로 최대 힙을 구성합니다(heapfify).
- 최대 힙의 루트노드와 마지막 리프노드를 교환합니다.
- 새 루트노드에 대해 최대 힙을 구성합니다.
- 원소의 수만큼 해당 과정을 반복 수행합니다.
시간복잡도는 최초로 최대 힙을 만드는 시간복잡도는 O(n)입니다.
힙이 구성된 상태에서 각 노드에 대해 heapify를 수행하는 경우, 마지막 리프노드가 루트노드에 올라오기까지
트리의 높이(logn)만큼 자리 이동을 해야 합니다. 이렇게 heapify를 해야 하는 노드들이 n개가 있으므로
시간복잡도는 O(nlogn)이 됩니다. 따라서 O(n)+O(nlogn)이며 결과적으로 O(nlogn)이 됩니다.
장점
- 시간 복잡도가 으로 빠른 편에 속합니다O(nlogn).
- 가장 큰 값을 구하거나 가장 작은 값을 구할 때 좋습니다.
단점
- 실제 프로그램에 구현할 경우 평균 계산 속도가 퀵 정렬보다 느리고 불안정합니다.
def heap_sort(arr):
def heapify(arr, index, heap_size):
largest = index
left_index = 2 * index + 1
right_index = 2 * index + 2
if left_index < heap_size and arr[left_index] > arr[largest]:
largest = left_index
if right_index < heap_size and arr[right_index] > arr[largest]:
largest = right_index
if largest != index:
arr[largest], arr[index] = arr[index], arr[largest]
heapify(arr, largest, heap_size)
n = len(arr)
# BUILD-MAX-HEAP (A)
# 인덱스 : (n을 2로 나눈 몫-1)~0
# 최초 힙 구성시 배열의 중간부터 시작하면
# 이진트리 성질에 의해 모든 요소값을
# 서로 한번씩 비교할 수 있게 됨 : O(n)
for i in range(n // 2 - 1, -1, -1):
heapify(arr, i, n)
# Recurrent (B) : 2~4단계
# 한번 힙이 구성되면 개별 노드는
# 최악의 경우에도 트리의 높이(logn)
# 만큼의 자리 이동을 하게 됨
# 이런 노드들이 n개 있으므로 : O(nlogn)
for i in range(n - 1, 0, -1):
arr[0], arr[i] = arr[i], arr[0]
heapify(arr, 0, i)
return arr
'기술 관련 정리' 카테고리의 다른 글
트랜잭션이란? (0) | 2022.04.12 |
---|---|
정렬 알고리즘 - 계수 정렬(Counting Sort) (0) | 2022.04.09 |
정렬 알고리즘 - 병합 정렬(Merge Sort) (0) | 2022.04.09 |
정렬 알고리즘 - 퀵 정렬(Quick Sort) (0) | 2022.04.09 |
정렬 알고리즘 - 삽입 정렬(Insertion Sort) (0) | 2022.04.09 |