최소 힙이란?
부모 노드가 자식 노드보다 항상 작은 트리 구조를 말한다.
완전 이진 트리이고, 배열로 구현된다. 배열로 구현된다는 말은, 부모 노드의 index가 parentIndex라고 하면 왼쪽 자식 노드의 인덱스는 parentIndex*2
, 오른쪽 자식 노드의 인덱스는 parentIndex*2+1
이라는 의미이다.
삽입
최고 깊이에서 가장 오른쪽에 삽입된다. 새로 삽입된 노드의 부모 노드를 계속 bottom-up 하면서, 부모 노드가 자신보다 더 클 경우 부모 노드와 자신 노드를 swap한다.
최소 값 꺼내기
최소 값(루트 노드)을 꺼낸다음, 트리의 가장 루트 노드를 가장 마지막으로 삽입된 값(최고 깊이에서 가장 오른쪽 노드)로 대체한다. 그 다음, 힙정렬의 성질을 유지하기 위해 루트 노드에서 부터 자식 노드들을 top-down 하며 자신보다 더 작은 자식 노드가 있으면 자신과 자식노드를 swap한다. 만약 왼쪽 자식과 오른쪽 자식이 둘 다 자신보다 작다면, 둘 중에 더 작은 자식과 swap 해야한다.
코드
가장 루트 노드의 인덱스를 1로 설정하는게 코드가 훨씬 더 깔끔하고 복잡하지 않다.
#include <iostream>
using namespace std;
class MinHeap
{
private:
int* tree;
int capacity;
int size;
public:
MinHeap(int capacity) : capacity(capacity), size(0)
{
tree = new int[capacity];
}
~MinHeap()
{
delete []tree;
}
// 배열의 크기를 확장한다.
void extend()
{
int* tmp = new int[size];
for(int i=0;i<size;i++)
tmp[i] = tree[i];
capacity *= 2;
tree = new int[capacity];
for(int i=0;i<size;i++)
tree[i] = tmp[i];
}
void insert(int value)
{
size++;
int childPos = size;
int parentPos = childPos/2;
if(size >= capacity)
extend();
tree[childPos] = value;
while(parentPos > 1 && tree[childPos] < tree[parentPos])
{
swap(tree[childPos], tree[parentPos]);
childPos = parentPos;
parentPos = childPos/2;
}
}
int extractMin()
{
int min = tree[1];
// 최소값 노드를 가장 마지막 요소로 대체한다.
tree[1] = tree[size];
tree[size] = 0;
size--;
// 힙 트리를 유지한다.
int parentPos = 1;
int leftPos = parentPos*2;
int rightPos = parentPos*2 + 1;
// 왼쪽 자식이 있을 경우에만 균형 맞추기를 한다.
// 왼쪽 자식이 없으면 오른쪽 자식도 없기 때문이다.(힙은 완전 이진 트리)
while(leftPos <= size)
{
int targetPos;
if(rightPos > size) // 오른쪽 자식이 없는 경우
{
if(tree[parentPos] < tree[leftPos])
break;
else
targetPos = leftPos; //왼쪽 자식 선택
}
else //양쪽 자식 다 있는 경우
{
//이미 자식이 둘 다 커버리면 균형 맞추기를 종료한다.
if(tree[parentPos] < tree[leftPos] && tree[parentPos] < tree[rightPos])
break;
else
targetPos = (tree[leftPos] < tree[rightPos]) ? leftPos : rightPos; // 양쪽 자식 중에 더 큰 녀석과 부모와 교환한다.
}
swap(tree[targetPos], tree[parentPos]);
// 부모 위치와 자식 위치를 새로 갱신한다.
parentPos = targetPos;
leftPos = targetPos*2;
rightPos = targetPos*2 + 1;
}
return min;
}
void printAll()
{
for(int i=1;i<=size;i++)
cout<<tree[i]<<" ";
cout<<endl;
}
};
int main()
{
MinHeap mh(10);
mh.insert(1);
mh.insert(7);
mh.insert(10);
mh.insert(9);
mh.insert(5);
mh.insert(6);
mh.printAll();
cout<<mh.extractMin()<<endl;
cout<<mh.extractMin()<<endl;
cout<<mh.extractMin()<<endl;
cout<<mh.extractMin()<<endl;
cout<<mh.extractMin()<<endl;
cout<<mh.extractMin()<<endl;
}