우선순위 큐 (Priority Queue)
정의
우선순위 큐 (Priority Queue) 는 원소 중 최댓값 (또는 최솟값) 을 O(log N) 에 추출하는 자료구조. 내부 구현은 대개 힙 (heap).
- Max-Heap: 부모 ≥ 자식. 루트가 최댓값.
- Min-Heap: 부모 ≤ 자식. 루트가 최솟값.
C++ STL std::priority_queue 는 기본 max-heap. Python heapq 는 기본 min-heap.
문제 상황과 동기
“항상 최댓값 (또는 최솟값) 을 빠르게 꺼내야” 하는 상황:
- naive: 정렬 후 꺼낸다. 삽입마다 정렬 O(N log N), 쿼리 O(1). 삽입 많으면 비효율.
- 정렬 없이 최댓값: 선형 탐색 O(N). 쿼리가 Q 개면 O(N·Q).
- priority queue (heap): push O(log N), pop O(log N), top O(1). 삽입 + 추출 모두 로그.
핵심 통찰: 트리 구조 (부모 ≥/≤ 자식) + 완전 이진 트리 + 배열 표현 → 삽입/삭제 모두 O(log N), 캐시 친화적.
시각화
핵심 아이디어
힙은 완전 이진 트리 (complete binary tree) 로, 배열에 저장:
인덱스 i 노드의 부모: (i-1)/2 (0-indexed)
왼쪽 자식: 2i+1
오른쪽 자식: 2i+2
Max-Heap invariant: a[parent] ≥ a[child] 모든 부모-자식 쌍에서 성립.
연산
-
push(x) (upheap / bubble-up):
- 배열 끝에 x 추가.
- 부모와 비교, 크면 swap. 루트 또는 부모 ≥ x 될 때까지 반복.
- O(log N).
-
pop() (downheap / bubble-down):
- 루트 제거 (최댓값).
- 배열 마지막 원소를 루트로 올림.
- 두 자식 중 큰 쪽과 비교, 작으면 swap. 자식 없거나 자식 ≤ 현재값 될 때까지 반복.
- O(log N).
-
top(): 배열 첫 원소 (루트). O(1).
Amortized analysis: 각 원소가 트리 높이 log N 만큼만 이동. N 번 push + N 번 pop 총 O(N log N).
알고리즘
push(x):
arr.append(x)
i = len(arr) - 1
while i > 0 and arr[parent(i)] < arr[i]:
swap(arr[i], arr[parent(i)])
i = parent(i)
pop():
max_val = arr[0]
arr[0] = arr.back()
arr.pop_back()
i = 0
while True:
left = 2*i + 1, right = 2*i + 2
largest = i
if left < len(arr) and arr[left] > arr[largest]:
largest = left
if right < len(arr) and arr[right] > arr[largest]:
largest = right
if largest == i:
break
swap(arr[i], arr[largest])
i = largest
return max_val
top():
return arr[0]
구현
// C++ std::priority_queue (max-heap 기본)
#include <bits/stdc++.h>
using namespace std;
int main() {
int n; cin >> n;
priority_queue<int> pq; // max-heap
for (int i = 0; i < n; i++) {
int x; cin >> x;
pq.push(x);
}
// 최댓값부터 순서대로 pop
while (!pq.empty()) {
cout << pq.top() << " ";
pq.pop();
}
}5
3
1
4
1
55 4 3 1 1복잡도
| 항목 | 값 |
|---|---|
| push | O(log N) |
| pop | O(log N) |
| top | O(1) |
| 공간 | O(N) |
| build heap (N 개 원소) | O(N) (heapify) |
변형 / 활용
| 응용 | 설명 |
|---|---|
| Dijkstra | 최단 거리 노드를 O(log N) 에 추출. Dijkstra. |
| Prim | 최소 간선 비용 노드를 O(log N) 에 추출. MST. |
| K번째 원소 | min-heap 크기 K 유지. Top K. |
| 중앙값 유지 | max-heap (왼쪽) + min-heap (오른쪽). Median of Stream. |
| 이벤트 시뮬레이션 | 시각 순으로 이벤트 처리. |
| Huffman 코딩 | 빈도 낮은 노드부터 합치기. |
함정
1. C++ vs Python 기본 순서
C++ priority_queue 는 max-heap. Python heapq 는 min-heap. 혼동 주의.
Python 에서 max-heap 은 heappush(pq, -x), heappop(pq) 후 - 붙여 원복.
2. Java PriorityQueue 는 기본 min-heap
max-heap 은 new PriorityQueue<>(Collections.reverseOrder()) 또는 (a, b) -> b - a.
3. top 후 pop 안 하기
top 은 값만 확인. 제거는 pop. 둘 다 해야 한다.
4. 중복 원소
힙은 중복을 허용. 같은 값 여러 개 push 가능. 필요시 set 과 함께 써서 중복 제거.
5. 힙 정렬 (Heap Sort) 은 O(N log N) 이지만 캐시 비효율
같은 O(N log N) 이어도 quick sort / merge sort 가 일반적으로 빠름. 힙은 주로 “부분 정렬” (top K) 에 쓴다.
BOJ 연습 문제
| 번호 | 제목 | 정답률 | 링크 |
|---|---|---|---|
| BOJ 11279 | 최대 힙 | - | kokoa-lab |
| BOJ 11286 | 절댓값 힙 | - | kokoa-lab |
| BOJ 1927 | 최소 힙 | - | kokoa-lab |
| BOJ 1655 | 가운데를 말해요 (중앙값 유지) | - | kokoa-lab |
참고
이 글의 용어 (4개)
- 다익스트라 알고리즘 (Dijkstra's Algorithm)algorithm
- 정의 다익스트라 알고리즘 (Dijkstra's Algorithm) 은 음이 아닌 가중치 그래프에서 단일 시작점 s 로부터 모든 정점까지의 최단 거리를 찾는 그리디 알고리즘. Ed…
- 최소 신장 트리 (MST, Minimum Spanning Tree)algorithm
- 정의 최소 신장 트리 (Minimum Spanning Tree, MST) 는 연결 그래프 G = (V, E) 에서 모든 정점을 연결 하면서 간선 가중치 합이 최소 인 부분 그래프…
- Median of Stream: 두 힙으로 중앙값 유지algorithm
- 정의 스트림으로 들어오는 정수의 중앙값을 O(log N) 에 유지하는 문제. 두 힙 트릭 - Max-heap : 아래 절반 - Min-heap : 위 절반 - 크기 차 <= 1 …
- Top K Selection: 상위 K개 원소algorithm
- 정의 배열 또는 스트림에서 상위 K 개 원소를 뽑는 문제. 접근 방법 1. 정렬 후 자르기 O(N log N). K 가 작을 때 낭비 심함. 2. Min-Heap (크기 K) O…
💬 댓글