[python] 우선순위 큐 PriorityQueue VS heapq

2024. 9. 22. 00:41·python

⭐️ 1. 우선순위 큐?

우선순위가 높은 데이터가 먼저 나가는 (제일 위에 놓임) 큐

즉, 제일 첫번째 원소가 제일 우선순위가 높은 것

 

구현 방식

- Heap, List, ... 등 구현 방식이 있다.

 

시간복잡도에 차이가 존재한다.

List의 경우

삽입: O(1), 삭제: O(N)

 

Heap의 경우

삽입: O(logN), 삭제:O(logN)

 

✅ 사용 예시

- 스케줄링, 사건 시뮬레이션, 우선순위에 따른 검색, 정렬 등

 

✅ 규칙 (최소 힙 기준)

  • 부모가 두 자식보다 더 작은 수임이 보장된다. (부모가 min)
  • 완전 이진 트리를 만족한다.

최소 힙

 

✅ 동작 (회소 힙 기준)

추가 (push) 동작

- 맨 마지막 원소로 추가한다

- 해당 위치에서 root까지 순회하며, min heap을 만족하는지 체크한다

 

시간복잡도: O(logN)

 

 

 

삭제 (pop)

- 맨 앞 원소를 삭제한다

- 빈 맨 앞 원소를 '제일 마지막 원소'로 채워준다 (마지막 노드 삭제)

- 새로운 root부터 시작하여, 재귀적으로 두 자식과 비교하여 min 값을 자신과 교체함

 

시간복잡도: O(logN)

 

 


⭐️ 2. PriorityQueue 

- 파이썬의 우선순위 큐 구현체 (모듈)

- 이진 트리 형태

- 최소 힙

    - 부모 노드 값 <= 자식 노드의 값

 

✅ PriorityQueue생성 (instance)

from queue import PriorityQueue
pq = PriorityQueue()

 

  • PriorityQueue 클래스 instance 사용

 

 

그럼 pq에 원소를 집어넣고, .. 하기 위해

 

PriorityQueue class의 method를 보자!

 

 

✅ 주요 메소드 (PriorityQueue) - 직접 호출 X

def _put(self, item):
        heappush(self.queue, item)
def _get(self):
        return heappop(self.queue)

- 원소 추가: pq._put()

- 원소 삭제: pq._get()

 

Ququq Class를 상속받는 PriorityQueue Class

 

 

✅ 주요 메소드 (호출 흐름)

🙋‍♀️ PriorityQueue의 _put을 부르면 되겠구나?

 

그런데,

_put 메소드는 private method 이므로 (method 앞에 _ 붙으면 private method)

pq._put() 으로 바로 호출할 수 없다 ! 

(사실 부를 수 있긴 하다)

 

 

🙋‍♀️ PriorityQueue의 부모인 Queue Class의 put을 부르면 되겠구나?

맞다!

PriorityQueue의 부모 클래스의 메소드도 사용 가능하므로,

Queue의 put을 부르면 된다.

 

 

🙋‍♀️ 그럼 heapify는 언제 되는거지?

 

Queue class의 put 메소드를 살펴보자.

 

put 함수 내부에서 _put 매소드를 부른다.

이때 Queue에도, PriorityQueue에도 둘 다 _put 메소드가 존재한다.

 

이때 메소드 오버라이딩에 의해,

PriorityQueue의 _put 이 우선순위가 더 높기 때문에, 

PriorityQueue의 _put이 불리게 된다!

 

PriorityQueue의 _put에서는 heappush 동작이 수행된다.

즉, 이때 heapify가 진행된다

 

 

✅  결론 1. put 쓰자 (써도 heapify 잘 된다)

put 쓰자
put 메소드(Queue)에서 _put 메소드(PriorityQueue) call

 

 

✅  결론 2. (내부구현) heappush, heappop을 호출한다

PriorityQueue 클래스 메소드를 살펴보자.

from heapq import heappush, heappop


class PriorityQueue(Queue):
	# ... (중략)
    def _put(self, item):
        heappush(self.queue, item)

    def _get(self):
        return heappop(self.queue)

 

굳이 Queue의 put을 거치는 이유?

Queue의 put이 thread-safe 하다.

 

Queue에서는 init 시, 이런 mutex lock을 정의한다.

self.not_full = threading.Condition(self.mutex)

 

그리고 Queue의 put 메소드는, 

lock을 얻은 상태에서만 실행된다.

  def put(self, item, block=True, timeout=None):
        with self.not_full:
            if self.maxsize > 0:
            	'''생략'''

 

즉, thread-safe하기 때문에 Queue의 put을 거치는 것이다.

 

 

✅ 사용 예시

from queue import PriorityQueue

pq = PriorityQueue()

# 원소 추가
pq.put(2)
pq.put(1)
pq.put(3)


# 원소 삭제
pq.get() # 1
pq.get() # 2
pq.get() # 3
pq.get() # 무한 반복에 걸림 (주의)
pq._get() # 무한 반복에 걸리진 않지만, 권장 x (private method)

 


⭐️ 3. heapq

- 파이썬의 우선순위 큐 구현체 (모듈)

- 이진 트리 형태

- 최소 힙

    - 부모 노드 값 <= 자식 노드의 값

 

 

✅ heapq생성

import heapq

heap = []
# heapq 메소드의 인자로 heap list 넘김

 

위에서 본 PriorityQueue와 달리, 

리스트를 heap처럼 다룬다.

 

heapq의 method의 인자로 리스트를 넘기는 방식으로 사용된다.

 

 

그럼 heapq의 method를 보자!

 

 

✅ 주요 메소드 (heapq)

def heappush(heap, item):
    heap.append(item)
    _siftdown(heap, 0, len(heap)-1)
def heappop(heap):
    lastelt = heap.pop() # 맨 마지막 원소 pop
    
    if heap:
        # 리스트가 비어 있지 않다면 수행
        returnitem = heap[0]
        # 힙 구조 유지
        heap[0] = lastelt
        _siftup(heap, 0)
        return returnitem
                
    # 리스트가 비어 있다면 리스트의 마지막 원소 반환
    return lastelt
def heapify(x):
    # list를 heap으로 전환.
    
    n = len(x)
    for i in reversed(range(n//2)):
        _siftup(x, i)

- 원소 추가: heappush()

- 원소 삭제: heappop()

- 힙 구조 유지(list -> heap): heapify()

 

주의:

원소 추가, 삭제 후 heapify 함수 부를 필요 X

list를 heap으로 변경하기 위해 heapify 함수를 부름

 

+ heappush, heappop 함수의 인자로 사용되는 list는 

heapify가 되었다 가정.

 

 

✅ 사용 예시

import heapq

# 힙 생성
heap = []

# 원소 추가
heapq.heappush(heap, 2)
heapq.heappush(heap, 1)

# 원소 삭제 (가장 작은 원소)
item = heapq.heappop(heap) # 1

# 가장 작은 원소 출력 (원소 삭제 X)
item = heap[0]

# list를 heap으로 변형
listA = [3, 1, 6]
heapq.heappush(listA, 2) # listA는 heapify가 되었다 가정하므로 정상동작 x # 3, 1, 6, 2
heapq.heapify(listA) # listA 리스트 heapify

 


⭐️ 4. PriorityQueue VS heapq 한 줄 정리

PriorityQueue VS heapq 비교

위에서도 다루어 보았듯,

PriorityQueue는 thread-safe 하다는 점에 차이가 있다.

 


⭐️ 5.기본 (min 순서대로) 사용하기

 

✅ PriorityQueue

from queue import PriorityQueue

pq = PriorityQueue()

# 원소 추가
pq.put(2)
pq.put(1)
pq.put(3)


# 원소 삭제
pq.get() # 1
pq.get() # 2
pq.get() # 3

 

✅ heapq

import heapq

# 힙 생성
heap = []

# 원소 추가
heapq.heappush(heap, 3)
heapq.heappush(heap, 1)

# 원소 삭제 (가장 작은 원소)
item = heapq.heappop(heap) # 1

⭐️ 6. 역순 (max 순서대로) 사용하기

 

✅ PriorityQueue

from queue import PriorityQueue

pq = PriorityQueue()

# heap으로 생성할 원소들
items = [3, 1, 2]

for item in items:
	pq.put(-item) # 음수로 입력

# pq: [-3, -2, -1]

# 원소 삭제 (가장 큰 원소)
for _ in range(len(heap)):
	print(-pq.get()) # 3, 2, 1

 

✅ Heapq

import heapq

# 힙 생성
items = [3, 1, 2] # heap으로 생성할 원소들
heap = []

# 원소 추가
for item in items:
	heapq.heappush(heap, -item) # 음수로 입력
    
# heapq: [-3, -2, -1]

# 원소 삭제 (가장 큰 원소)
for _ in range(len(heap)):
	print(-heapq.heappop(heap)) # 3, 2, 1

⭐️ 7. 코딩 테스트에서는 heapq를 쓰자

heapq가 PriorityQueue보다 속도가 더 빠르다.

 

위에서도 다루어 보았듯,

PriorityQueue는 thread-safe 하기 때문이다. 

 


참고 자료

 

우선순위 큐 성능 시험에 관한 연구

python3.9 heapq.py 라이브러리

python3.9 queue.py 라이브러리

https://2jinishappy.tistory.com/102

 

자료구조 Heap(힙)이란? 기본 연산과 HeapSort, Heapify에 대해

Heap은 자료구조의 한 종류로써, 특정 property를 만족하는 Complete Binary Tree를 말한다 Heap의 어떤 node p에 대해, p의 parent node를 q라 하면 q의 value는 p의 value보다 작다 ❗❗ 이런 property를 만족하는 힙은

2jinishappy.tistory.com

https://dawitblog.tistory.com/160

 

[자료구조]우선순위 큐(Priority Queue)

정의 In computer science, a priority queue is an abstract data type similar to a regular queue or stack data structure in which each element additionally has a "priority" associated with it. In a priority queue, an element with high priority is served be

dawitblog.tistory.com

 

 

'python' 카테고리의 다른 글

[python] list - flatten  (1) 2024.01.02
[python] list - 2중, 3중 리스트 생성  (2) 2024.01.02
'python' 카테고리의 다른 글
  • [python] list - flatten
  • [python] list - 2중, 3중 리스트 생성
KyuminKim
KyuminKim
컴퓨터공학과 학생의 이모저모 개발 일지 📝
  • KyuminKim
    이모저모
    KyuminKim
  • 전체
    오늘
    어제
    • 분류 전체보기 (53)
      • 프로젝트 (2)
        • first-blog (2)
      • 클라우드 (22)
        • 도커 (14)
        • 쿠버네티스 (5)
        • AWS (2)
      • 알고리즘 (5)
        • 코드트리 (0)
        • 프로그래머스 (5)
      • 백엔드 (8)
      • 프론트엔드 (2)
      • 보안 (3)
        • 드림핵 (2)
      • python (3)
      • 네트워크 (1)
      • 기타 (6)
        • 2025 프로펙트 부트캠프(1차) | 클라우드 엔.. (0)
        • OSSCA | 2024 오픈소스 컨트리뷰션 아카데.. (0)
        • WIK (0)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    티스토리챌린지
    오블완
    알고리즘
    코드트리
    파이썬
    cannot send an empty message
    docker
    주간레포트
    EC2
    도커
    도커파일
    탈퇴구현
    amazonlinux
    2024 당근 테크 밋업
    쿠버네티스
    코딩트리조별과제
    urf8
    DP
    characterencoding
    DB
    코드트리조별과제
    진단평가
    인코딩
    자료구조
    apiserver-runtime
    고랭
    character_set_server
    MySQL
    코딩테스트
    recover_your_data
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.2
KyuminKim
[python] 우선순위 큐 PriorityQueue VS heapq
상단으로

티스토리툴바