본문으로 건너뛰기
김신건의 로그

우선순위 큐 (Priority Queue)

· 수정 · 📖 약 3분 · 882자/단어 #algorithm #data-structure #priority-queue #heap
priority queue, 우선순위 큐, 힙, heap

정의

우선순위 큐 (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] 모든 부모-자식 쌍에서 성립.

연산

  1. push(x) (upheap / bubble-up):

    • 배열 끝에 x 추가.
    • 부모와 비교, 크면 swap. 루트 또는 부모 ≥ x 될 때까지 반복.
    • O(log N).
  2. pop() (downheap / bubble-down):

    • 루트 제거 (최댓값).
    • 배열 마지막 원소를 루트로 올림.
    • 두 자식 중 큰 쪽과 비교, 작으면 swap. 자식 없거나 자식 ≤ 현재값 될 때까지 반복.
    • O(log N).
  3. 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();
  }
}
stdin
5
3
1
4
1
5
결과
5 4 3 1 1

복잡도

항목
pushO(log N)
popO(log N)
topO(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_queuemax-heap. Python heapqmin-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…

이 개념을 다룬 위키 페이지 (1)

💬 댓글

사이트 검색 / 명령어

검색

스크롤 = 확대/축소 · 드래그 = 이동 · 0 = 원래 크기 · ESC = 닫기