우선순위 큐란?
큐(Queue)는 먼저 들어오는 데이터가 먼저 나가는 FIFO(First In First Out) 형식의 자료구조이다.
우선순위 큐(Priority Queue)는 먼저 들어오는 데이터가 아니라, 우선순위가 높은 데이터가 먼저 나가는 형태의 자료구조이다.
우선순위 큐는 일반적으로 힙(Heap)을 이용하여 구현한다.
우선순위 큐는 최소한 두 가지 연산이 지원되어야 한다.
- 하나의 원소를 우선순위를 지정하여 추가하는 함수(push)
- 가장 높은 우선순위를 가진 원소를 큐에서 제거하고 반환하는 함수(pop)
우선순위 큐 구현방법 비교
우선순위 큐를 힙이 아니라 배열 또는 연결리스트를 이용하여 구현할 수도 있다.
하지만 배열과 연결리스트는 선형 구조의 자료구조이므로 삽입 또는 삭제 연산을 위한 시간복잡도는 O(n)이다.
반면 힙트리는 완전이진트리 구조이므로 힙트리의 높이는 log₂(n+1)이며, 힙의 시간복잡도는 O(log₂n)이다.
아래는 이를 정리한 표이다.
우선순위 큐 구현
힙 규현
힙은 일반적으로 배열을 이용하여 구현한다.
완전 이진트리이므로 중간에 비어있는 요소가 없기 때문이다.
위 그림과 같이 트리의 각 노드에 번호를 붙이고, 이 번호를 배열의 인덱스로 생각하면 효과적으로 힙을 구현할 수 있다.
배열로 구현하였기 때문에 부모 또는 자식 노드를 찾아가는 연산을 구현하기도 간편하다.
자식노드를 구하고 싶을 때
- 왼쪽 자식노드 index = (부모 노드 index) * 2
- 오른쪽 자식노드 index = (부모 노드 index) * 2 + 1
부모노드를 구하고 싶을 때
- 부모 노드 index = (자식노드 index) / 2
삽입연산
힙에 삽입을 하기 위해서는 힙 트리의 성질을 만족시키면서 새로운 요소를 추가해야 한다.
삽입 방법
- 우선 완전이진트리의 마지막 노드에 이어서 새로운 노드를 추가한다.
- 추가된 새로운 노드를 부모의 노드와 비교하여 교환한다.
- 정상적인 힙트리가 될 때 까지 (더이상 부모노드와 교환할 필요가 없을 때까지) 2번을 반복한다.
최악의 경우 새로 추가된 노드가 루트노트까지 비교하며 올라가야 하므로 시간복잡도가 O(log₂n)이다.
삭제 연산
힙 트리에서 루트노드가 가장 우선순위가 높으므로 루트 노드를 삭제해야 한다.
삭제가 이뤄진 후 힙 트리의 성질이 유지돼야 하므로 아래와 같은 방법으로 삭제를 진행한다.
삭제 방법
- 루트 노드를 삭제한다.
- 루트 노드가 삭제된 빈자리에 완전이진트리의 마지막 노드를 가져온다.
- 루트 자리에 위치한 새로운 노드를 자식 노드와 비교하여 교환한다.
이때 최대 힙인 경우 자식노드 중 더 큰 값과 교환을 하며, 최소 힙인 경우 더 작은 값과 교환을 한다. - 정상적인 힙트리가 될 때까지 (더 이상 자식노드와 교환할 필요가 없을 때까지) 3번을 반복한다.
삭제 연산 또한 최악의 경우 루트노트부터 가장 아래까지 내려가야 하므로 시간복잡도가 O(log₂n)이다.
출처-https://suyeon96.tistory.com/31
파이썬에서의 우선순위 큐
파이썬에서는 우선순위 큐의 활용을 위해, 모듈을 제공하고 있다.
PriorityQueue
from queue import PriorityQueue
q = PriorityQueue()
q1 = PriorityQueue(maxsize=10) # maxsize를 활용하면 크기 제한 가능
put
- .put(item)
q.put(3) # 원소를 넣는 과정
q.put(4)
q.put(1)
q1.put((1, 'apple')) # (우선순위, 값)의 형태로 저장할 수도 있음
get
- .get()
# 원소 삭제 및 반환
q.get() # 1
q1.get()[1] # (우선순위, 값)의 형태에서 값 반환
Heapq
자세한 내용은 https://docs.python.org/ko/3/library/heapq.html 참조
- 최소 힙(Min Heap)의 구조
- 모든 k에 대해 heap[k] <= heap[2*k+1] 또는 heap[k] <= heap[2*k+2] 만족
- 가장 작은 요소가 heap[0]에 위치
- 힙을 만들기 위해서는, 초기화된 리스트 []를 사용하거나, heapify를 통해 값이 들어있는 리스트를 힙으로 변환 가능
import heapq
hq = []
push
- heapq.heappush(heap, item)
- 힙의 형태를 유지하면서 item을 push
heapq.heappush(hq, 4)
heapq.heappush(hq, 1)
heapq.heappush(hq, 3)
heapq.heappush(hq, 7)
pop
- heapq.heappop(heap)
- 힙의 형태를 유지하면서 가장 작은 항목을 pop, 반환
- 비어있으면 IndexError 발생
- 반환하지 않고 접근하고 싶다면, heap[0] 활용
heapq.heappop(hq) # 1
heapify
- heapify(x)
- 리스트 x를 선형 시간으로 제자리에서 heap으로 변환
x = [4, 3, 1, 2, 5, 6]
print(x) # [4, 3, 1, 2, 5, 6]
heapq.heapify(x)
print(x) # [1, 2, 4, 3, 5, 6]
'1일1CS' 카테고리의 다른 글
6.유니캐스트, 멀티캐스트, 브로드캐스트 (0) | 2022.09.09 |
---|---|
5. Scale-up과 Scale-out의 개념 (0) | 2022.09.09 |
4. 캐시의 지역성 (0) | 2022.09.08 |
3. OSI 7계층 (0) | 2022.09.07 |
2.큐에 비해 원형큐가 가지는 장단점 (0) | 2022.09.07 |
댓글