본문 바로가기

2024 코딩테스트 스터디

[9주_3일차] 프로그래머스-이중우선순위큐(Python)

문제 설명

이중 우선순위 큐는 다음 연산을 할 수 있는 자료구조를 말합니다.


이중 우선순위 큐가 할 연산 operations가 매개변수로 주어질 때, 모든 연산을 처리한 후 큐가 비어있으면 [0,0] 비어있지 않으면 [최댓값, 최솟값]을 return 하도록 solution 함수를 구현해주세요.

제한사항
  • operations는 길이가 1 이상 1,000,000 이하인 문자열 배열입니다.
  • operations의 원소는 큐가 수행할 연산을 나타냅니다.
    • 원소는 “명령어 데이터” 형식으로 주어집니다.- 최댓값/최솟값을 삭제하는 연산에서 최댓값/최솟값이 둘 이상인 경우, 하나만 삭제합니다.
  • 빈 큐에 데이터를 삭제하라는 연산이 주어질 경우, 해당 연산은 무시합니다.

입출력 예 #1

  • 16과 -5643을 삽입합니다.
  • 최솟값을 삭제합니다. -5643이 삭제되고 16이 남아있습니다.
  • 최댓값을 삭제합니다. 16이 삭제되고 이중 우선순위 큐는 비어있습니다.
  • 우선순위 큐가 비어있으므로 최댓값 삭제 연산이 무시됩니다.
  • 123을 삽입합니다.
  • 최솟값을 삭제합니다. 123이 삭제되고 이중 우선순위 큐는 비어있습니다.

따라서 [0, 0]을 반환합니다.

입출력 예 #2

  • -45와 653을 삽입후 최댓값(653)을 삭제합니다. -45가 남아있습니다.
  • -642, 45, 97을 삽입 후 최댓값(97), 최솟값(-642)을 삭제합니다. -45와 45가 남아있습니다.
  • 333을 삽입합니다.

이중 우선순위 큐에 -45, 45, 333이 남아있으므로, [333, -45]를 반환합니다.

 

 

<코드>

import heapq

def solution(operations):
    min_heap = []
    max_heap = []
    entry_finder = {}  # 현재 힙에 있는 유효한 항목을 추적

    for operation in operations:
        command, num = operation.split()
        num = int(num)
        
        if command == 'I':
            heapq.heappush(min_heap, num)
            heapq.heappush(max_heap, -num)
            if num in entry_finder:
                entry_finder[num] += 1
            else:
                entry_finder[num] = 1
        
        elif command == 'D':
            if num == 1 and max_heap:
                # 최댓값 삭제
                while max_heap and entry_finder.get(-max_heap[0], 0) == 0:
                    heapq.heappop(max_heap)
                if max_heap:
                    max_val = -heapq.heappop(max_heap)
                    entry_finder[max_val] -= 1
                    if entry_finder[max_val] == 0:
                        del entry_finder[max_val]
            
            elif num == -1 and min_heap:
                # 최솟값 삭제
                while min_heap and entry_finder.get(min_heap[0], 0) == 0:
                    heapq.heappop(min_heap)
                if min_heap:
                    min_val = heapq.heappop(min_heap)
                    entry_finder[min_val] -= 1
                    if entry_finder[min_val] == 0:
                        del entry_finder[min_val]
    
    # 최종 결과 계산
    while min_heap and entry_finder.get(min_heap[0], 0) == 0:
        heapq.heappop(min_heap)
    while max_heap and entry_finder.get(-max_heap[0], 0) == 0:
        heapq.heappop(max_heap)
    
    if not min_heap or not max_heap:
        return [0, 0]
    
    min_val = min_heap[0]
    max_val = -max_heap[0]
    
    return [max_val, min_val]

 

<풀이과정>

  • min_heap:최소 힙(최솟값 검색)
  • max_heap: 최대 힙 (값을 음수로 저장하여 최소 힙처럼 동작하도록 함)
  • entry_finder: 힙에 있는 항목을 추적하는 딕셔너리로, 각 항목의 개수를 저장
  • 리스트의 값을 command와 숫자로 분리
  • 주어진 숫자를 min_heap과 max_heap에 삽입(max_heap에는 음수로 변환하여 삽입)
  • entry_finder에서 해당 숫자의 개수를 증가시키거나 새로 추가

 

  • D 1: 최대 힙에서 최댓값 삭제, entry_finder에서 유효한 항목만 처리
  • D -1: 최소 힙에서 최솟값 삭제, entry_finder에서 유효한 항목만 처리

 

  • 힙에서 유효하지 않은 항목을 제거 후 두 힙이 비어 있는지 확인

 

* entry_finder의 역할

- 항목의 직접 삭제는 힙의 규칙을 깨뜨릴 수 있으며, 이를 조정하려면 전체 힙을 재구성해야 하기에 상태를 추적해서 이후에 삭제하는 것이 효율적

  • 지연 삭제:
    • 항목을 삽입할 때, 최소 힙과 최대 힙에 추가
    • 항목을 삭제할 때는 항목이 실제로 힙에서 제거되지 않고, entry_finder에서 카운트를 감소시킨 후 실제 삭제는 힙에서 유효한 항목만 확인할 때 수행
  • 유효하지 않은 항목 제거:
    • 최솟값 또는 최댓값을 삭제할 때, 힙에서 유효하지 않은 항목을 건너뛰고, 유효한 항목만 삭제
    • 힙에서 유효하지 않은 항목을 제거하여 최종적으로 유효한 값만 남도록