Quick Reference
| 연산 | 비용 | 의미 |
|---|---|---|
| peek | O(1) | 가장 우선순위 높은 값 확인 |
| enqueue | O(log n) | 새 값 추가 후 힙 조정 |
| dequeue | O(log n) | 루트 제거 후 힙 조정 |
| build heap | O(n) | 배열 전체를 힙으로 구성 |
var pq = new PriorityQueue<string, int>();
pq.Enqueue("B", 20);
pq.Enqueue("A", 10);
string first = pq.Dequeue(); // "A"PriorityQueue<TElement, TPriority>는 priority가 작은 항목을 먼저 꺼냅니다. 값의 전체 정렬이 아니라 다음 하나의 최솟값·최댓값을 반복해서 고를 때 사용하며, 같은 priority의 순서는 보장하지 않습니다.
구조
힙은 완전 이진 트리 형태를 유지하면서 부모와 자식 사이의 우선순위 규칙을 만족하는 자료구조입니다. 최소 힙에서는 부모가 자식보다 작거나 같습니다.
1
/ \
3 5
/ \
7 9힙은 전체가 정렬된 구조가 아닙니다. 오직 루트가 가장 작은 값이라는 보장만 있습니다. 그래서 최솟값을 반복해서 꺼내는 문제에는 좋지만, 임의 위치의 값을 정렬 순서로 탐색하는 데에는 맞지 않습니다.
배열로 표현
힙은 보통 배열로 표현합니다. 0-based index 기준으로 부모와 자식 위치를 계산할 수 있습니다.
parent = (i - 1) / 2
left = i * 2 + 1
right = i * 2 + 2삽입할 때는 끝에 넣고 위로 올리며, 삭제할 때는 마지막 값을 루트로 옮긴 뒤 아래로 내립니다.
언제 쓰나
| 상황 | 적합한 선택 |
|---|---|
| 매번 최솟값·최댓값 필요 | 우선순위 큐 |
| 최단 경로 후보 관리 | 다익스트라 |
| 상위 K개 유지 | 크기 K 힙 |
| 전체 정렬만 필요 | 정렬 API가 더 단순 |
| 임의 값 삭제가 잦음 | 별도 인덱스 관리 필요 |
C#의 PriorityQueue<TElement, TPriority>는 priority가 작은 항목을 먼저 꺼내는 최소 우선순위 큐입니다. 최댓값을 먼저 꺼내고 싶으면 priority를 음수로 넣거나 비교 기준을 별도로 설계합니다.
var descending = Comparer<int>.Create((a, b) => b.CompareTo(a));
var maxPq = new PriorityQueue<int, int>(descending);
maxPq.Enqueue(10, 10);
maxPq.Enqueue(30, 30);
int max = maxPq.Dequeue(); // 30priority를 음수로 바꾸는 방식은 int.MinValue를 반전할 때 overflow가 날 수 있습니다. 비교자를 넘기면 priority 값 자체의 의미를 유지할 수 있습니다. 비어 있을 가능성이 있으면 예외를 던지는 Peek·Dequeue 대신 TryPeek·TryDequeue를 씁니다.
while (pq.TryDequeue(out string? element, out int priority))
{
Console.WriteLine($"{element}: {priority}");
}
pq.EnqueueRange(new[]
{
(Element: "A", Priority: 10),
(Element: "B", Priority: 20)
});
string removed = pq.EnqueueDequeue("C", 15); // C를 넣은 뒤 최솟값 하나를 바로 꺼냄TryDequeue는 빈 큐에서 false를 반환하고 element와 priority를 함께 받을 수 있습니다. EnqueueRange는 여러 element-priority 쌍을 넣을 때, EnqueueDequeue는 넣은 뒤 즉시 최솟값 하나를 제거해야 할 때 두 번의 호출보다 효율적일 수 있습니다. 둘 다 일반 Enqueue·Dequeue와 결과 순서가 같다는 전제를 먼저 확인하고 사용하세요.
주의할 점
우선순위 큐는 같은 priority의 dequeue 순서를 안정적으로 보장하는 정렬 큐가 아닙니다. 같은 우선순위의 순서가 중요하면 tie-breaker를 priority에 함께 넣거나 별도 순번을 관리해야 합니다.
또 PriorityQueue<TElement, TPriority>는 .NET 6 이상 API입니다. 제출 환경이 낮은 버전이면 직접 힙을 구현해야 할 수 있습니다.
참고 링크
1 sources