Priority Queue
·
cs
우선순위 큐란?
- 큐와 다르게 FIFO(선입선출) 하는 구조가 아니라 우선순위가 높은 순서에 따라 원소를 반환한다.
- 큐도 먼저 삽입된 원소가 우선순위가 더 높은 일종의 우선순위큐로도 생각해볼 수 있다.
- 보통 이진힙(Binary Heap)으로 구현된다.
이진힙(Binary Heap)
- 완전이진트리로서 부모노드의 우선순위가 자식노드의 우선순위보다 높은 자료구조
- Minimum Heap(최소힙) : 키값이 작을수록 높은 우선순위를 가짐
- Maximum Heap(최대힙) : 키값이 클수록 높은 우선순위를 가짐
이진힙의 구현
- 1차원 리스트를 활용해 구현할 수 있다. 1차원 리스트에서 두번째 원소부터 사용하여 구현한다.
- 완전이진트리의 노드를 레벨순회(Level-Order Traversal) 방향으로 배열에 차례대로 저장한다.

- 배열을 x라고 하였을떄 i번쨰 노드(원소)가 가지는 특징은 다음과 같다.
1. x[i] 의 자식노드는 x[2i] , x[2i+1] 에 위치한다.
2. x[i] 의 부모노드는 x[i//2]에 위치한다. (단, i가 루트노드가 아닌 경우)
이진힙의 연산
- 이진힙은 항상 부모노드의 우선순위가 자식노드의 우선순위보다 높아야 한다. 때문에 heap에 원소를 넣거나, 뺼떄 이 규칙을 준수하도록 연산이 이루어져야 한다. 이떄 heap 내 원소 교환 연산이 이루어지는 방향에 따라 downheap,upheap으로 나눌 수 있다.
핵심 연산
- downheap 연산 : 부모노드에서 자식노드 방향으로 내려가면서 , 왼쪽자식노드와 오른쪽자식노드간 우선순위가 더 높은 승자를 뽑는다. 그리고 승자자식노드를 부모노드와 교환한다.
- upheap 연산 : 자식노드에서 부모노드 방향으로 올라가면서 , 자식노드의 우선순위가 더 높다면 부모노드와 교환
이진힙의 구현 예시
- 이진힙을 1차원 리스트를 활용해 구현하면 다음과 같다. (최소 힙 구현이며, 최대 힙은 각 원소의 -1을 곱해주면 구현이 가능하다)
- insert시 upheap 연산을 수행하면서, 리프 노드에서 부모노드 방향으로 우선순위 정합성을 지키도록 연산이 이루어진다.
- pop시 downheap 연산을 수행하면서, 루트노드에서 리프노드 방향으로 우선순위 정합성을 지키도록 연산이 이루어진다.
public class BinaryHeap {
private int[] heap;
private int size;
public BinaryHeap(int capacity) {
this.heap = new int[capacity];
this.size = 0;
}
public void insert(int value) {
size++;
if (size >= this.heap.length) {
this.heap = Arrays.copyOf(heap, heap.length * 2);
}
heap[size] = value;
upHeap(size);
}
public int pop() {
if (size == 0) {
throw new IllegalStateException("heap is empty");
}
int root = heap[1];
heap[1] = heap[size];
heap[size--] = 0;
downHeap(1);
return root;
}
private void downHeap(int index) {
while (2 * index <= size) {
int k = 2 * index;
if (k < size && heap[k] > heap[k + 1]) {
k += 1;
}
if (heap[index] < heap[k] || heap[k] == 0) {
break;
}
int tmp = heap[index];
heap[index] = heap[k];
heap[k] = tmp;
index = k;
}
}
private void upHeap(int index) {
while (index > 1 && heap[index / 2] > heap[index]) {
int tmp = heap[index / 2];
heap[index / 2] = heap[index];
heap[index] = tmp;
index /= 2;
}
}
}
class BinaryHeapTest {
@Test
public void should_pop_top_priority_element() {
BinaryHeap heap = new BinaryHeap(5);
List<Integer> input = List.of(17, 3, 2, 1, 59, 6, 23, 12, 32);
for (int value : input) {
heap.insert(value);
}
List<Integer> sortedInput = input.stream().sorted().toList();
for (Integer value : sortedInput) {
assertThat(heap.pop()).isEqualTo(value);
}
assertThatThrownBy(heap::pop);
}
}
연산 시간복잡도
- 우선순위가 가장 높은 원소에 접근 : O(1)
- 삼입/삭제 연산 : 힙의 높이, 즉 완전이진트리의 깊이에 비례한다. O(logN)
Java Built-In Library
- java 에서는 우선순위큐를 직접 구현할 필요없이 구현체를 제공하고 있다.
java.util.PriorityQueue
우선순위 자료구조를 활용한 코딩테스트 문제
Reference
[1] https://runestone.academy/ns/books/published/pythonds/Trees/BinaryHeapImplementation.html