알고리즘/자료구조

[C++/ Algorithm] # 힙 (heap)

쪼성윤 2024. 11. 20. 20:47

학습목표 

  1. 우선순위 큐를 위하여 만들어진 자료구조, 힙(heap)에 대해 이해한다.
  2. 배열을 이요하여 힙(heap)을 구현할 수 있다.
  3. 힙(heap)의 삽입과 삭제를 이해한다.

 

들어가기 전

  • 우선순위 큐: 우선순위의 개념을 큐에 도입한 자료구조'

--> 데이터들이 우선순위를 가지고 있고 우선순위가 높은 데이터가 먼저 나간다.

 

 

  • 우선순위 큐는 배열, 연결리스트, 으로 구현이 가능하다. 이 중에서 힙(heap)으로 구현하는 것이 가장 효율적이다.

 

 

 

 

자료구조 '힙(heap)'이란?

  • 완전 이진트리의 일종으로, 우선순위 큐를 위하여 만들어진 자료구조이다.
  • 여러 개의 값들 중에서 최댓값이나 최솟값을 빠르게 찾아내도록 만들어진 자료구조이다.
  • 힙은 일종의 반정렬 상태 (느슨한 정렬 상태)를 유지한다.

--> 큰 값이 상위 레벨이 있고 작은 값이 하위 레벨에 있다는 정도

--> 간단히 말하면 부모 노드의 키 값이 자식 노드의 키 값보다 항상 큰 (작은) 이진트리를 말한다.

  • 힙 트리에서는 중복된 값을 허용한다. (이진 탐색 트리에서는 중복된 값을 허용하지 않는다.)

 

힙 (heap)의 종류

  • 최대 힙 (max heap)

--> 부모 노드의 키 값이 자식 노드의 키 값보다 크거나 같은 완전 이진 트리

--> key (부모 노드) >= key (자식 노드)

 

  • 최소 힙 (min heap)

--> 부모 노드의 키 값이 자식 노드의 키 값보다 작거나 같은 완전 이진 트리

--> key (부모 노드) <= key (자식 노드)

 

 

 

 

 

힙(heap)의 구현

  • 힙을 저장하는 표준적인 자료구조는 배열이다. 
  • 구현을 쉽게 하기 위하여 배열의 첫 번째 인덱스인 0은 사용되지 않는다.
  • 특정 위치의 노드 번호는 새로운 노드가 추가되어도 변하지 않는다.
예를 들어, 루트 노드의 오른쪽 노드의 번호는 항상 3이다.

 

  • 힙에서의 부모 노드와 자식 노드의 관계
왼쪽 자식의 인덱스 = 부모의 인덱스 * 2
오른쪽 자식의 인덱스 = 부모의 인덱스 * 2 + 1
부모의 인덱스 = 자식의 인덱스 / 2

 

 

 

 

힙(heap)의 삽입

  1. 힙에 새로운 요소가 들어오면, 일단 새로운 노드를 힙의 마지막 노드에 이어서 삽입한다.
  2. 새로운 노드를 부모 노드들과 교환해서 힙의 성질을 만족시킨다. 

아래의 최대 힙(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)의 삭제

  1. 최대 힙에서 최댓값은 루트 노드이므로 루트 노드가 삭제된다.
최대 힙 (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

 

[자료구조] 힙(heap)이란 - Heee's Development Blog

Step by step goes a long way.

gmlwjd9405.github.io

https://twpower.github.io/137-heap-implementation-in-cpp

 

[Algorithm] C/C++에서 힙(Heap) 구현하기

Practice makes perfect!

twpower.github.io