GOAL
- Heap 에 대한 이해
- 우선순위 큐에 대한 이해와 응용
✨ Heap 은 Binary Heap 이라고도 불리는 자료구조
- 정렬에도 사용되며, 최악의 경우 시간 복잡도가
- Sort in place 로 Quick Sort나 Merge Sort와 다르게 추가 메모리를 사용하지 않음
힙은 일차원 배열로 표현 가능하며, 루트는 Heap[1], Heap[i]의 부모는 Heap[i/2], Heap[i]의 왼쪽 자식은 Heap[2i], 오른쪽 자식은 Heap[2i+1] 로 표현할 수 있다.
✨ Heap을 이용해서 정렬할 때 필요한 기본 연산으로 Root를 제외한 모든 Node 가 Heap Property를 만족한다고 가정
i
의 두 자식 Node 중 더 큰 값을 가지는 것의 Index 를 k
로 둔다. Heap Property
를 만족한다. Heap Property
를 만족하지 않는 경우 위 두 과정을 반복한다. Max-Heapify(A,i){
if there is no child of A[i]
return;
k <- index of **the beggest** child of i;
if A[i] >= A[k] return;
exchange A[i] and A[k];
Max-Heapify(A,k);
}
Max-Heapify(A,i){
while A[i] has a child do
k <- index of the biggest child of i;
if A[i] >= A[k] return;
exchange A[i] and A[k];
i = k;
end;
}
✨ 시간 복잡도: O(NlogN)
HeapSort(A){
build-max-heap // O(N) 주어진 데이터를 힙으로 만든다.
for i <- heap-size down to 2 do // N-1 번, 루트 값 (최댓값)을 맨 마지막 값과 바꾼다.
exchange A[i] and A[i] // O(1)
heap-size <- heap-size - 1 // O(1) 마지막 값은 힙의 일부가 아닌 것으로 간주 (이미 정렬)
Max-Heapify(A,1) // O(logN) Heapify 연산
}
✨ Insert 연산과 Extract 연산으로 구성되며 시간 복잡도는 모두 O(NlogN)
새로 들어온 값을 가장 마지막 노드에 추가하고 Hepify 연산을 수행한다.
Max-Heap-Insert(A,key){
heap-size = heap-size + 1;
A[heap-size] = key;
i = heap-size ; // 새로 들어온 node의 index
while ( i > 1 and A[Parent(i)] < A[i]){
exchange A[i] and A[Parent(i)];
i = Parent(i);
}
}
루트 노드의 값을 반환 및 제거하고 Heapify 연산을 수행한다.
Heap-Extract-Max(A){
if heap_size[A] < 1 {
then error "heap underflow"
}
max <- A[1]
A[1] <- A[heap_size[A]]
heap_size[A] <- heap_size[A] -1
Max_Heapify(A,1)
return max
}
참고
Index Error
heap[0]
h = []
heappush(h, (5, 'write code'))
heappush(h, (7, 'release product'))
heappush(h, (1, 'write spec'))
heappop(h) # (1, 'write spec')
Quize
- 동일한 값이 추가되었을 때, 정렬 상태에서 값이 추가된 순서대로 유지되는 것을 stable sort라고 한다. 우선 순위 큐는 Stable Sort 인가 ?
- 아닙니다. Stable Sort를 위해서는 값이 추가된 순서를 별도로 가지고 있는 등의 처리가 필요합니다. Stable Sort에는
Insertion
,Merge
,Bubble
,Counting
Sort가 있습니다.
참고
Priority-Queue 에서는 heapq 를 Wrapper 하여 사용하고 있다.
Priority-Queue는 Thread Safe 하고, heapq는 Non-Safe
연관 문제