우선순위 큐(priority queue)란 FIFO의 특징을 가진 큐(queue)에 우선순위 방식을 접목시킨 자료구조입니다.
즉, 먼저 들어온 원소가 아닌 우선순위가 높은 원소가 먼저 나가는 특징을 가집니다.
우선순위 큐는 힙(heap)이라는 자료구조를 통해 구현할 수 있습니다. 그렇다면 힙은 무엇일까요?
힙(heap)이란 우선순위 큐를 위해 고안된 완전 이진트리 형태의 자료구조입니다. 트리에 대해 모르신다면, 뒤쪽의 트리 파트를 먼저 보시고 오시길 바랍니다.
힙의 종류에는
1️⃣ 최대 힙: 부모 노드의 키 값이 자식 노드보다 크거나 같은 완전 이진트리
2️⃣ 최소 힙: 부모 노드의 키 값이 자식 노드보다 작거나 같은 완전 이진트리
가 있습니다.
최대 힙이 default인 C++과는 다르게, python에서 힙은 최소 힙이 default입니다. 이는 뒤의 연습문제에서 다시 한 번 확인해보도록 하겠습니다.
힙은 앞서 배웠던 덱과는 다르게 모듈에서 자료구조 자체를 제공해주진 않습니다. 하지만 리스트를 힙처럼 사용할 수 있도록, 모듈에서 여러가지 함수를 제공합니다.
from heapq import heappush, heappop
대표적으로 heappush()와 heappop() 함수가 있습니다. 이름 그대로 heappush()는 원소 삽입시에, heappop()은 원소 삭제시에 사용하게 됩니다. 자세한 사용법을 알아봅시다.
위처럼 heappush(hq, 값)의 방식으로 힙에 요소를 삽입할 수 있습니다.
원소가 삽입되기만 하는 게 아니라, 삽입되면서 최소 힙으로 새롭게 힙 정렬되는 모습을 주석을 통해 확인할 수 있습니다.
백문이 불여일타 ! 😎 직접 hq를 조회하며 과정을 확인해보세요.
heappush할 때 어떻게 자동으로 정렬되는 것일까?
아래는 C++ 최대 힙 기준으로 [2,7,15,18,3,22,5]의 원소를 하나씩 넣는 과정에서 heapq가 새롭게 정렬되는 모습입니다.
heapq는 한 원소를 넣을 때마다 O(logN)의 시간복잡도를 가지는 것을 알 수 있습니다. 이진트리를 이용해 정렬하는 과정이니 당연하겠죠? 비교와 조정의 과정에서 힙의 높이 O(logN) 만큼을 소요하는 것을 아래에서 확인할 수 있습니다.
heappop
삭제는 heappop(hq)의 방식으로 사용하면 됩니다. heappop()은 실행 시에 부모 노드의 값이 삭제되고, 그 값을 반환하게 됩니다.
heappush와 마찬가지로 삭제 시에도 최소 힙으로 새롭게 힙 정렬됩니다.
heappify
그렇다면 빈 리스트에 값을 하나씩 넣는(heappush) 작업 대신, 이미 원소가 정해진 리스트를 힙으로 바꿀 순 없을까요?
heapify()를 사용하면 위와 같이, 리스트 내부의 원소들이 힙 구조에 맞게 재배치됩니다. 이 때 heapify()는 모든 원소들을 새롭게 조정해야 하므로 O(N)의 시간복잡도를 가집니다.
from heapq import heappop
hq = [1, 2, 3, 4]
print(heappop(hq)) # 1 출력
print(hq) # [2, 4, 3] 출력
from heapq import heapify
hq = [4, 2, 3, 1]
heapify(hq)
print(hq) # [1, 2, 3, 4] 출력
from heapq import heappush, heappop
n = int(input())
hq = []
for _ in range(n):
val = int(input())
if val != 0:
heappush(hq, val)
continue
if not hq:
print(0)
else:
print(heappop(hq))
from heapq import heappush, heappop
n = int(input())
hq = []
for _ in range(n):
val = int(input())
if val != 0:
heappush(hq, -val)
continue
if not hq:
print(0)
else:
print(-heappop(hq))
from heapq import heappush, heappop
n = int(input())
hq = []
for _ in range(n):
val = int(input())
if val != 0:
heappush(hq, [-val, val])
continue
if not hq:
print(0)
else:
print(heappop(hq)[1])
from heapq import heappush, heappop
n = int(input())
hq = []
for _ in range(n):
val = int(input())
if val != 0:
heappush(hq, (abs(val), val))
continue
if not hq:
print(0)
else:
print(heappop(hq)[1])