힙(Heap)은 최댓값, 최솟값을 찾아내는 연산을 빠르게 하기 위해 고안된 완전 이진트리(Complete Binary Tree)를 기반으로 한 자료구조입니다.
힙은 두 종류가 있습니다.
- Max Heap : 부모 노드의 값이 자식 노드의 값보다 크거나 같은 완전 이진트리
- Min Heap : 부모 노드의 값이 자식 노드의 값보다 작거나 같은 완전 이진트리
Heap이란 단어는 OS에서 프로세스, JVM의 메모리 영역 중 Heap 영역을 통해 많이 들어봤습니다. 언뜻 생각하기에 이 것들이 같은 걸 말하는 건가 해서 알아봤습니다.
일단 프로세스, JVM의 메모리 영역의 Heap과 자료구조 힙은 아예 다른 것이었습니다.
메모리 영역 중 힙은 동적으로 메모리를 할당받는 메모리 영역을 말합니다. 메모리가 비연속적인 공간을 사용하며 프로그램 실행 중에 동적으로 메모리를 할당하고 해제할 수 있습니다. Java에서 new연산자를 통해 Heap에 메모리를 할당할 수 있습니다.
프로그래밍에서 일반적으로 Heap이라고 하면 자료구조 힙이 아니라 메모리 영역 Heap을 말한다고 합니다.
Heap을 구현하는 여러 방법이 있지만 일반적으로 배열(Array), 완전 이진트리(Complete Binary Tree)를 통해 구현된다고 합니다.
배열을 사용한 Heap은 일차원 배열로 구현되며, 각 노드는 배열의 인덱스에 해당합니다. 루트 노드는 항상 인덱스 0에 위치하며, 자식 노드는 각각 왼쪽 자식과 오른쪽 자식으로 배열의 인덱스를 이용하여 계산됩니다. 배열을 사용한 Heap은 메모리를 덜 사용하고 삽입/삭제 연산이 빠른 장점이 있습니다.
완전 이진트리를 사용한 Heap은 이진트리(Binary Tree) 중에서도 완전 이진트리(Complete Binary Tree)를 사용합니다. 완전 이진트리는 노드가 왼쪽에서부터 차례대로 채워지는 이진트리로, Heap에서는 루트 노드가 완전 이진트리의 맨 위에 위치하며, 자식 노드는 왼쪽부터 채워지기 때문에 배열과 유사한 구조를 가지고 있습니다.
그러나 완전 이진트리 또한 Array로 구현하는 것이 Linked List로 구현하는 것보다 삽입, 삭제 연산이 빠르기 때문에 Heap은 Array로 구현하면 됩니다. Linked List로 구현하는게 좋은 케이스는 노드의 개수가 너무 많아지는 경우 배열로 구현하기 어렵기 때문에 이 때는 Linked List로 구현하는게 좋습니다.
Java에서는 PriorityQueue 클래스를 이용하여 우선순위 큐를 구현할 수 있습니다. PriorityQueue 클래스는 Heap을 이용하여 구현되며, 기본적으로 최소 힙(Min Heap)을 사용합니다.
import java.util.PriorityQueue;
public class PriorityQueueExample {
public static void main(String[] args) {
// 우선순위 큐 생성
PriorityQueue<Integer> pq = new PriorityQueue<Integer>();
// 원소 추가
pq.add(3);
pq.add(1);
pq.add(5);
// 최소값 반환 및 삭제
System.out.println(pq.poll()); // 1
// 원소 추가
pq.add(2);
// 전체 출력
while (!pq.isEmpty()) {
System.out.println(pq.poll());
}
// 2, 3, 5
}
}
Heap으로 구현된 우선순위 큐는 다음과 같은 곳에서 사용됩니다.