학습목표
- 우선순위 큐를 위하여 만들어진 자료구조, 힙(heap)에 대해 이해한다.
- 배열을 이요하여 힙(heap)을 구현할 수 있다.
- 힙(heap)의 삽입과 삭제를 이해한다.
들어가기 전
- 우선순위 큐: 우선순위의 개념을 큐에 도입한 자료구조'
--> 데이터들이 우선순위를 가지고 있고 우선순위가 높은 데이터가 먼저 나간다.
- 우선순위 큐는 배열, 연결리스트, 힙으로 구현이 가능하다. 이 중에서 힙(heap)으로 구현하는 것이 가장 효율적이다.
자료구조 '힙(heap)'이란?
- 완전 이진트리의 일종으로, 우선순위 큐를 위하여 만들어진 자료구조이다.
- 여러 개의 값들 중에서 최댓값이나 최솟값을 빠르게 찾아내도록 만들어진 자료구조이다.
- 힙은 일종의 반정렬 상태 (느슨한 정렬 상태)를 유지한다.
--> 큰 값이 상위 레벨이 있고 작은 값이 하위 레벨에 있다는 정도
--> 간단히 말하면 부모 노드의 키 값이 자식 노드의 키 값보다 항상 큰 (작은) 이진트리를 말한다.
- 힙 트리에서는 중복된 값을 허용한다. (이진 탐색 트리에서는 중복된 값을 허용하지 않는다.)
힙 (heap)의 종류
- 최대 힙 (max heap)
--> 부모 노드의 키 값이 자식 노드의 키 값보다 크거나 같은 완전 이진 트리
--> key (부모 노드) >= key (자식 노드)
- 최소 힙 (min heap)
--> 부모 노드의 키 값이 자식 노드의 키 값보다 작거나 같은 완전 이진 트리
--> key (부모 노드) <= key (자식 노드)
힙(heap)의 구현
- 힙을 저장하는 표준적인 자료구조는 배열이다.
- 구현을 쉽게 하기 위하여 배열의 첫 번째 인덱스인 0은 사용되지 않는다.
- 특정 위치의 노드 번호는 새로운 노드가 추가되어도 변하지 않는다.
예를 들어, 루트 노드의 오른쪽 노드의 번호는 항상 3이다.
- 힙에서의 부모 노드와 자식 노드의 관계
왼쪽 자식의 인덱스 = 부모의 인덱스 * 2
오른쪽 자식의 인덱스 = 부모의 인덱스 * 2 + 1
부모의 인덱스 = 자식의 인덱스 / 2
힙(heap)의 삽입
- 힙에 새로운 요소가 들어오면, 일단 새로운 노드를 힙의 마지막 노드에 이어서 삽입한다.
- 새로운 노드를 부모 노드들과 교환해서 힙의 성질을 만족시킨다.
아래의 최대 힙(max heap)에 새로운 요소 8을 삽입해 보자.
void push(int data) {
// 힙의 가장 끝에 데이터 추가
heap[++heap_count] = data;
// 아래의 과정은 child를 parent와 비교하면서 알맞은 위치로 하나씩 올리는 부분
int child = heap_count;
int parent = child / 2;
while (child > 1 && heap[parent] < heap[child]) {
swap(&heap[parent], &heap[child]);
child = parent;
parent = child / 2;
}
}
힙(heap)의 삭제
- 최대 힙에서 최댓값은 루트 노드이므로 루트 노드가 삭제된다.
최대 힙 (max heap)에서 삭제 연산은 최댓값을 가진 요소를 삭제하는 것이다.\
2. 삭제된 루트 노드에는 힙의 마지막 노드를 가져온다.
3. 힙을 재구성한다.
아래의 최대 힙 (max heap)에서 최댓값을 삭제해 보자.
int pop() {
// 힙의 가장 첫번째 원소를 반환
// 힙의 가장 앞만 보고 있다!
int result = heap[1];
// 첫번째 원소를 힙의 가장 끝에 원소와 바꾸고
// 가장 끝은 이제 쓰지 않을 예정이니 heap_count를 내려준다.
swap(&heap[1], &heap[heap_count]);
heap_count--;
// 아래의 과정은 child를 parent와 비교하면서 알맞은 위치로 하나씩 내리는 부분
int parent = 1;
int child = parent * 2;
if (child + 1 <= heap_count) {
child = (heap[child] > heap[child + 1]) ? child : child + 1;
}
while (child <= heap_count && heap[parent] < heap[child]) {
swap(&heap[parent], &heap[child]);
parent = child;
child = child * 2;
if (child + 1 <= heap_count) {
child = (heap[child] > heap[child + 1]) ? child : child + 1;
}
}
return result;
}
참고: https://gmlwjd9405.github.io/2018/05/10/data-structure-heap.html
https://twpower.github.io/137-heap-implementation-in-cpp