[Programmers/ level3] 이중우선순위큐

2 분 소요


👩문제

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

명령어 수신 탑(높이)
I 숫자 큐에 주어진 숫자를 삽입합니다.
D 1 큐에서 최댓값을 삭제합니다.
D -1 큐에서 최솟값을 삭제합니다.

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


👩제한사항

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


👩입출력 예

operation return
[“I 16”,”D 1”] [0,0]
[“I 7”,”I 5”,”I -5”,”D -1”] [7,5]


🙋‍♀️내 풀이: heap 사용

우선 명령어에 따라서 최대값/최소값 삭제가 이루어지므로 해당 연산이 빠르게 이루어질 수 있는 heap을 사용해야 한다 생각했다.

heap을 구현하기 위해서 heapq를 사용하였다. heapq에 대해서는 다음 게시글에서 자세히 다루었으니 참고하길 바란다!!

여기서 고려해야 할 점은 어떻게 최댓값을 제거할 것인가이다. 단순히 힙의 가장 마지막 원소최대값이 아니라는 점을 주목해야 한다.

처음 나는 다음과 같이 알고리즘을 작성하였다.

import heapq

def solution(operations):
    heap = []
    for i in operations:
        op, val = i.split(" ")
        if op == "I":
            heapq.heappush(heap, int(val))
        elif len(heap) > 0 and val == "-1":
            heapq.heappop(heap)
        elif len(heap)> 0  and val == "1":
            if len(heap) == 1:
                heap.pop()
            else: 
                a = heap[-1]
                b = heap[-2]
                if a>b: heap.pop(-1)
                else: heap.pop(-2)
            heapq.heapify(heap)
            
    return [0,0] if len(heap) == 0 else [max(heap[-1], heap[-2]), heap[0]]

🙄 처음 위와 같이 최대값을 삭제할 때 마지막 두 원소만을 비교해 큰 값을 최대값으로 판단하였고, 해당 풀이가 정답처리가 되었다…!!

하지만 다음과 같은 경우, 이 풀이는 옳지 않다는 것을 깨닫게 되었다.

다른 사람들의 풀이를 확인하면서 두 힙을 두는 풀이가 효율적이라는 것을 알 수 있었다. 그렇다면 다음 알고리즘도 확인해보자!


🙋‍♀️다른 풀이1: 최대힙, 최소힙

우선 최대값을 삭제하기 편한 최대힙 하나, 최소값을 삭제하기 편한 최소힙 하나를 둔다.

☝️ 값을 넣을 때는 최대힙, 최소힙 모두에 넣는다. 단, 최대힙에는 -값을 넣음으로써 최대값이 가장 앞에 존재하도록 한다.

✌️ 최소값을 삭제할 때는 최소힙에서 heappop을, 최대값을 삭제할 때는 최대힙에서 heappop을 진행한다.

  • 단! 🌟힙의 동기화🌟를 해주어야 한다!!
    • 삭제하는 값이 다른 힙에서도 삭제되도록 remove를 사용한다.
    • remove를 사용할 경우, 힙 구조가 깨질 수 있으므로 🌟heapify🌟를 적용한다.


from heapq import heappush, heappop, heapify

def solution(arguments):
    max_heap = []
    min_heap = []
    for i in arguments:
        op, val = i.split(" ")
        if op == "I":
            heappush(max_heap, -int(val))
            heappush(min_heap, int(val))
        elif op == "D":
            try:
                if val == "1":
                    min_heap.remove(-max_heap[0])
                    heapify(min_heap)
                    heappop(max_heap)
                else:
                    max_heap.remove(-min_heap[0])
                    heapify(max_heap)
                    heappop(min_heap)
            except:
                continue
    if len(max_heap) == 0:
        return [0, 0]
    else:
        return [-heappop(max_heap), heappop(min_heap)]


🙋‍♀️다른 풀이2: heaq.nlargest 함수 사용

heaq에서는 n개의 가장 큰 요소/ 가장 작은 요소들로 구성된 리스트를 반환하는 함수가 존재한다.

👩 heapq.nlargest(n, iteralbe, key= None)

  • 데이터 내 n개의 가장 요소로 구성된 리스트를 반환한다.
  • key: 각 요소 비교 key를 설정할 수 있다.
    • key = str.lower일 경우, sorted(iterable, key= key, reverse = True)[:n]과 동일하다.


👩 heapq.nsmallest(n, iterable, key= None)

  • 데이터 내 n개의 가장 작은 요소로 구성된 리스트를 반환한다.
  • key: 각 요소 비교 key를 설정할 수 있다.
    • key = str.lower일 경우, sorted(iterable, key= key, reverse = True)[:n]과 동일하다.
import heapq

def solution(m, n, puddles):
    lst = [1, 5, 6, 8, 9]
    print(heapq.nlargest(2, lst, key = lambda x: -x)) #	[1, 5]
    print(heapq.nlargest(1, lst)) # [9]
    print(heapq.nsmallest(3, lst)) # [1, 5, 6]


👩 heapq.nlargest를 사용한 풀이

import heapq

def solution(operations):
    heap = []
    for i in operations:
        op, val = i.split(" ")
        if op == "I":
            heapq.heappush(heap, int(val))
        elif len(heap) > 0:
            if val == "1":
                heap.pop(heap.index(heapq.nlargest(1, heap)[0])) #가장 큰 값의 인덱스를 사용해서 값 삭제
                heapq.heapify(heap) #삭제 후 힙 재정렬
            else:
                heapq.heappop(heap)
    if len(heap) == 0:
        return [0, 0]
    else:
        return [max(heap), heap[0]]
            

🙇‍♀️풀이에 부족한 부분이 있다면 말씀해주세요! 감사합니다!


📃 참고

댓글남기기