Heap tree는 완전이진트리 형태로 모든 부모 노드의 값이 자식 노드 보다 크거나 (max heap) 모든 부모 노드의 값이 자식 노드 보다 작은 (min heap) tree를 말한다. 이 글에서는 max heap tree를 만드는 방법을 알아본다.
Heap tree를 생성하는 buildHeapTree()이 동작하는 절차는 다음과 같다. N은 원소의 갯수.
부모_노드 := 소숫점_버림(N / 2) - 1 부터 0까지
모든 부모_노드들에 대하여
hepify(부모_노드):
큰_자식 := max(왼쪽_자식, 오른쪽_자식)
if(큰_자식 > 부모_노드)
swap(큰_자식, 부모_노드)
heapify(큰_자식)
재귀로 구현된 heapify()가 등장하는데, 이 함수가 하는 역할은 주어진 node를 부모로 시작해서 단말 node로 내려가면서 max heap 조건에 맞도록 변경해 주는 역할을 한다. heapify()는 큰 자식과 부모를 swap() 하는 경우 재귀하므로 시간 복잡도는 O(트리높이)가 된다.
Heap tree를 생성하는 방법을 고민해 본 적이 있다면, 부모 node들을 순회 할 때 역순으로 순회하는게 얼마나 영리한 방법인지 무릎을 쳤을 것이다. 내 얘기다;;
예제
임의의 정렬되지 않은 배열 { 2, 7, 5, 4, 1, 6, 10, 3, 9, 8}을 예로들어 보자. 10개의 node를 가지는 tree를 배열로 구현 할 때, 부모 node들의 index는 소숫점_버림(N/2) – 1로 계산할 수 있다. 따라서 10개의 node를 가지는 이 tree의 부모 node들의 위치는 [0], [1], [2], [3], [4]이다. (C/C++로 구현 할때는 N/2한 값을 그냥 int형 변수에 넣으면 된다)
각 부모 node들과 자식 node들을 subtree라고 하면 이 예제는 다음과 같이 5개의 subtree들로 구분될 수 있다.
Heapify – Subtree A
부모인 4번째 index의 값과 하나 밖에 없는 자식인 9번째 index의 값을 비교한다. 자식의 값이 더 크므로 부모와 swap()한다.
Heapify – Subtree B
부모인 3번째 index와 왼쪽 자식인 7번째, 오른쪽 자식인 8번째를 비교해서 가장 큰 오른쪽 자식과 부모 node를 swap()한다.
Heapify – Subtree C
오른쪽 자식인 index 6번과 부모인 index 2번을 교체한다.
Heapify – Subtree D
1번 index의 왼쪽 자식이 더 크므로 부모와 swap()한다.
Heapify – Subtree E
2번 index의 값이 더 커서 부모인 0번 index와 swap()하고 보니 2번 index를 부모로 하는 subtree가 더이상 max heap이 아니게 된다.
그래서 다시 한번 heapify()를 호출해서 max heap을 만든다.
Code
/*
Name: buildHeapTree
Desc.: Makes sure all sub-trees to be max heapified.
Args.:
- arr : Pointer to array to be sorted.
- arrSize : Size of the array.
*/
static void buildHeapTree(int* arr, int arrSize)
{
int parentIdx = (arrSize / 2) - 1;
int arrayLastIdx = arrSize - 1;
for(; parentIdx >= 0; parentIdx--)
{
maxHeapify(arr, arrayLastIdx, parentIdx);
}
}
/*
Name: swap
Desc.: Exchanges two elements of the array 'arr' that two given indices indicates.
Args.:
- arr: Pointer to array to be sorted.
- idx1, idx2 : Two indices to be swapped.
*/
static void swap(int* arr, int idx1, int idx2)
{
int t = std::move(arr[idx1]);
arr[idx1] = std::move(arr[idx2]);
arr[idx2] = std::move(t);
}
/*
Name: maxHeapify
Desc.: Makes given tree and it's sub-trees to be max heap trees.
Args:
- arr : Pointer to array to be sorted.
- arrayLastIdx : Last index of the given array.
- parentIdx : Index of the parent node of the tree.
*/
static void maxHeapify(int* arr, int arrayLastIdx, int parentIdx)
{
int lcIdx = (parentIdx * 2) + 1; // Index for left child node.
int rcIdx = lcIdx + 1; // Index for right child node.
int biggerChildIdx = lcIdx; // Index for child that has bigger value.
// Stop recursion if left child index exceeds last index of the array.
if(lcIdx > arrayLastIdx)
return;
// Left child would be the only one if there's no right child.
if(rcIdx > arrayLastIdx)
biggerChildIdx = lcIdx;
else // Which of left/right is bigger if both are exits?
biggerChildIdx = (arr[lcIdx] < arr[rcIdx]) ? rcIdx : lcIdx;
// Swap and heapify if child is bigger than parent.
if(arr[parentIdx] < arr[biggerChildIdx])
{
swap(arr, parentIdx, biggerChildIdx);
maxHeapify(arr, arrayLastIdx, biggerChildIdx);
}
return;
}