[Python] 자료구조 - 우선순위큐와 힙(Heap)
1. 우선순위 큐 (PriorityQueue)
- 파이썬에서의 우선순위 큐는 예전 포스트에서 다뤘듯이 queue 라이브러리에 내장되어있는 함수 PrioirtyQueue를 사용하여 쉽게 구현할 수 있다.
- 파이썬에서의 우선순위 큐는 최소값부터 get()하는 Min Heap과 같은 구조를 가지고 있다.
1.1. put() & qsize() & get()
- 우선순위 큐에는 input값으로 숫자는 들어갈 수 있지만, 대소비교가 불가능한 str형태 단독은 불가능하다.
Input:
from queue import PriorityQueue
Pque = PriorityQueue() # que = PriorityQueue(maxsize=8) : 우선순위큐의 사이즈를 8로 제한하고 싶을 때
Pque.put(5)
Pque.put(1)
Pque.put(15)
Pque.put(10)
print(Pque.qsize())
Output:
4
Input:
for i in range(Pque.qsize()):
print(Pque.get())
Output:
1
5
10
15
1.2. tuple활용
- 숫자를 오름차순이 아닌 우선순위를 부여하고 싶거나, str형태를 input으로 넣고 싶다면 다음과 같이 튜플을 사용한다.
Input:
from queue import PriorityQueue
Pque = PriorityQueue()
Pque.put((3, 'Apple'))
Pque.put((1, 'Banana'))
Pque.put((2, 'Cherry'))
Pque.put((5, 'Donuts'))
Pque.put((4, 'Egg'))
Pque.qsize()
Output:
5
Input:
for i in range(Pque.qsize()):
print(Pque.get()[1]) # 튜플 구조이므로 인덱스1번을 인덱싱해줌
Output:
Banana
Cherry
Apple
Egg
Donuts
2. 힙 (Heap)
- 파이썬에서의 힙은 heapq 라이브러리를 통해 힙 관련 함수를 사용할 수 있는데 이는 위의 우선순위 큐와 달리 자료구조 그 자체를 생성하는 것이 아닙니다.
- 힙 객체를 생성한다는 개념으로 알기보다는 그냥 빈 리스트를 생성한 후 이에 힙 관련함수를 적용해 최소 힙 (MinHeap)처럼 다룰 수 있게 해준다고 아는 편이 좋습니다.
- 물론 이미 생성되어있는 리스트도 힙처럼 다룰 수 있게 만들어주는 함수 heapify도 존재합니다.
- 아래에서도 설명하겠지만, str형태의 우선순위를 정하기 위해 튜플을 다뤄야하는 경우에는 우선순위 큐(PriorityQueue)를 사용하는 것이 바람직할 것이다.
2.1. heappush() & heappop()
Input:
import heapq
heap_list = []
heapq.heappush(heap_list, 5)
heapq.heappush(heap_list, 15)
heapq.heappush(heap_list, -5)
heapq.heappush(heap_list, 45)
heapq.heappush(heap_list, 35)
print(heap_list)
print(type(heap_list))
Output:
[-5, 15, 5, 45, 35]
<class 'list'>
- 출력결과를 보면, MinHeap 구조 개념과 같이 최소값을 맨 위의 rootnode에 놓아둔 듯한 모습과 나머지 원소들은 sort가 안 되어있음을 볼 수가 있다.
- 이러한 MinHeap과 같은 특징의 자료구조이지만, 실제 타입은 변함없이 리스트임을 볼 수 있다.
Input:
for i in range(len(heap_list)):
print(heapq.heappop(heap_list))
Output:
-5
5
15
35
45
2.2. heapify()
- heapify()함수를 통해 이미 생성된 리스트를 MinHeap 구조로 만들 수 있다. 하지만 이것 역시 MinHeap구조의 특징만을 반영한 것이지, 그 자체는 힙이 아닌 리스트 형태를 유지한다.
Input:
heap = [5, 15, -5, 45, 35]
heapq.heapify(heap)
print(heap)
print(type(heap))
Output:
[-5, 15, 5, 45, 35]
<class 'list'>
- list가 MinHeap 구조를 가지게 되면서 최소값 -5를 찾아 맨위의 rootnode로 놓아두었고, 원래 해당자리에 있던 5가 -5의 자리를 대신하게 됐음을 알 수 있다.
Input:
for i in range(len(heap)):
print(heap.pop(0))
heapq.heapify(heap)
Output:
-5
5
15
35
45
- list의 pop()메소드와 heapify() 함수를 통해 오름차순으로 숫자를 출력해낼 수 있었다.
결론
- 파이썬에서는 str형태 혹은 다른 형태의 자료까지 우선순위를 정할 수 있는 튜플을 넣어 자료구조를 형성하는 queue라이브러리의 우선순위 큐(PriorityQueue)가 범용성이 넓다고 생각한다.
- 파이썬의 우선순위 큐와 힙은 MinHeap의 구조만을 가지고 있다는 문제점이 있다.
- MaxHeap의 구조를 생성하기 위해서는 PriorityQueue를 사용하여 구조를 생성한 후, 우선순위를 나타내는 숫자에 -1을 곱하는 조치를 취하면 될 것이므로 역시 heapq라이브러리
- PriorityQueue에서 사용한 put(), get() 메소드의 시간복잡도는 O(logn)이다.
- heapq라이브러리 안의 함수들의 시간복잡도를 생각해보자면, heappush()와 heappop()은 역시 O(logn)의 시간복잡도를 가지지만, 함수 heapify()는 O(n)의 비교적 높은 시간복잡도를 갖게 된다.
- 따라서, 우선순위 큐 혹은 힙 구조를 새로 생성하고 다루려고 한다면 일반적으로 queue라이브러리의 PriorityQueue를 사용하게 될 것 같다.
- heapq라이브러리를 사용하게 되는 경우는 아마 새로운 힙 구조를 처음부터 생성하는 것이 아닌, heapify를 통해 이미 생성되어있는 리스트를 힙 구조로 바꿔야 할 때, heapq라이브러리를 사용하게 될 것 같다.
'[파이썬] 개념정리 > [파이썬] 자료구조' 카테고리의 다른 글
[Python] 변수와 리스트 (0) | 2021.01.20 |
---|---|
[Python] 자료구조 - 트리 (0) | 2020.07.08 |
[Python] 자료구조 - 덱(Deque) (0) | 2020.07.08 |
[Python] 자료구조 - 스택(Stack) (0) | 2020.07.08 |
[Python] 자료구조 - 큐(Queue) (0) | 2020.07.07 |