- 완전 이진트리의 한 종류로 우선순위 큐를 구현할 수 있는 자료구조
- 중복된 값을 허용한다 ( 이진탐색트리에선 불가하다)
- 주로 최대값과 최소값을 찾아낼때 사용된다
- 우선순위 큐 : 데이터들이 우선순위를 가지고, 우선순위가 높은 데이터가 먼저 나가는 구조



부모노드의 값이 항상 자식노드보다 작아야 한다
-> root가 제일 작은값이 된다 (최소값)

부모노드의 값이 항상 자식노드보다 커야한다
-> root가 제일 큰 값이 된다 (최대값)

template를 사용하여 클래스를 구현하였다
현재 들어온 값의 번호인 index 변수와 T타입 SIZE 크기의 배열 생성
생성자에서 초기화 해준다
#include <iostream>
#include <algorithm>
#define SIZE 8
using namespace std;
// Heap
// 최댓값 최솟값 구할때 사용
// 데이터를 뽑을때 노드 한번에 뽑을 수 있어서(인덱스) 상수시간
template <typename T>
class Heap
{
private:
int index;
T array[SIZE];
public:
// 생성자
Heap()
{
index = 0;
for (int i = 0; i < SIZE; i++)
{
array[i] = NULL;
}
}
void Insert(T data)
{
// 인덱스가 사이즈보다 커지면 오버플로우
if (index + 1 >= SIZE)
{
cout << "Heap OverFlow" << endl;
}
else
{
array[++index] = data;
int child = index;
int parent = child / 2;
// child가 부모보다 큰지 비교 (반복문), 크면 스왑
// 자식은 무조건 부모보다 작아야함
// 부모와 자식 노드 관계만 중요함
// 힙 정렬
while (child > 1)
{
if (array[child] > array[parent])
{
swap(array[parent], array[child]);
}
child = parent;
parent = child / 2;
}
}
}





// 최대값 뽑기 -> return
const T& Remove()
{
// 데이터가 없다면
if (index <= 0)
{
cout << "Heap is Empty" << endl;
exit(0);
}
else
{
// 임시변수 (담아주기)
// 젤 마지막 노드를 가져와서 뺀 자리 덮어씌우기
// 마지막 노드 자리는 NULL로
// 빼낸 값을 return 해야하기 때문에 임시변수 save 필요
T save = array[1];
array[1] = array[index];
array[index--] = NULL;
// 힙 정렬 (left = parent * 2 , right = parent * 2 + 1)
int parent = 1;
// 부모는 왼 오 둘중 큰 값과 스왑
while (parent * 2 <= index)
{
int child = parent * 2;
if (array[child] < array[child + 1])
{
child++;
}
if (array[child] < array[parent])
{
break;
}
else
{
swap(array[parent], array[child]);
parent = child;
}
}
return save;
}
}
index가 0보다 작거나 같다는 것은 힙이 비어있다는 것
힙이 비어있지 않다면
-> root노드는 최대값이 들어있기 때문에 임시변수를 만들고 [1] 인덱스의 값을 저장시켜둔다 (root노드에 저장된 값)
-> root 노드에 마지막 index의 값을 대입시켜준다
-> 마지막 노드의 값을 NULL로 만들어주고 index를 감소시킨다 (현재 인덱스 6)


이 상태에서는 힙 구조의 규칙에 맞지 않는다
-> root노드가 제일 값이 커야하지만 지금 제일 작다
-> 따라서 하향로직을 실행해서 높은 값과 스왑해주는 과정이 필요하다
parent[ ] 는 root노드인 1 부터 시작
-> child은 parent * 2

while(parent * 2 <= index) 조건은 child가 index의 값보다 작을 경우 실행한다는 의미이다
-> 즉, 불만족하는 조건은 child가 index값을 넘어가는 경우인데 이러면 힙을 초과한다는 의미기 때문
예를 들어, 아래의 힙 구조는 처음에 parent값과 child값을 비교한 뒤 스왑해준다
-> parent의 위치와 child의 위치값이 갱신되어 변한다
-> child의 값은 6으로 index보다 작거나 같아서 반복문 실행이 가능하다
-> 하지만 다음 parent의 위치가 6이 되었을때 parent * 2가 12가 되면서 index값을 넘어버리기 때문에 조건이 거짓이 된다 (반복문 실행X)


왼쪽 자식과 오른쪽 자식을 비교하여 오른쪽 자식이 더 큰 경우 child값을 증가
아래 사진의 경우 child의 값 30, child + 1의 값 40 으로 오른쪽 자식의 값이 더 큼
child를 오른쪽 자식으로 바꿔준다 child++
if (array[child] < array[child + 1]) { child++; }
child값과 parent 값을 비교하여 부모가 더 크다면 힙 조건이 성립하기 때문에 break문으로 탈출한다
if (array[child] < array[parent]) { break; }
만약 child의 값이 parent 값보다 크다면 서로의 값을 바꿔주어야 함
그리고 parent의 위치를 새로 바꿔주어야 한다
아래의 예시에서 child값이 부모보다 더 크기 때문에 서로 swap 해준 뒤 parent의 위치를 기존의 child 위치로 옮겨준다 (스왑된 값이 아래와 또 비교해야하기 때문이다)
새로운 parent의 위치가 정해졌으면 child값도 새로운 parent * 2의 위치가 된다
else { swap(array[parent], array[child]); parent = child; }
마지막으로 내가 save변수에 저장해두었던 기존의 root 노드 값인 최대값을 반환하면 된다
void Show()
{
for (int i = 1; i <= index; i++)
{
cout << array[i] << " ";
}
}

int main()
{
Heap <int> heap;
heap.Insert(10);
heap.Insert(20);
heap.Insert(30);
heap.Show();
cout << endl;
// 큰거 부터 뽑힘
cout << heap.Remove() << endl;
cout << heap.Remove() << endl;
cout << heap.Remove() << endl;
heap.Show();
return 0;
}