파이썬 알고리즘 인터뷰_데크 & 우선순위 큐


«파이썬 알고리즘 인터뷰 서적 내용을 정리»

데크(deque)

큐(queue)


문제01 원형 데크 디자인

https://leetcode.com/problems/design-circular-deque

다음 연산을 제공하는 원형 데크를 디자인하라.

  • MyCircularDeque(k) : 데크 사이즈를 k로 지정하는 생성자다.
  • insertFront() : 데크 처음에 아이템을 추가하고 성공할 경우 true를 리턴한다.
  • insertLast() : 데크 마지막에 아이템을 추가하고 성공할 경우 true를 리턴한다.
  • deleteFront() : 데크 처음에 아이템을 삭제하고 성공할 경우 true를 리턴한다.
  • deleteLast() : 데크 마지막에 아이템을 삭제하고 성공할 경우 true를 리턴한다.
  • getFront() : 데크의 첫 번째 아이템을 가져온다. 데크가 비어 있다면 -1을 리턴한다.
  • getRear() : 데크의 마지막 아이템을 가져온다. 데크가 비어 있다면 -1을 리턴한다.
  • isEmpty() : 데크가 비어 있는지 여부를 판별한다.
  • isFull() : 데크가 가득 차 있는지 여부를 판별한다.

Example 1:

MyCircularDeque circularDeque = new MycircularDeque(3); // set the size to be 3
circularDeque.insertLast(1);			// return true
circularDeque.insertLast(2);			// return true
circularDeque.insertFront(3);			// return true
circularDeque.insertFront(4);			// return false, the queue is full
circularDeque.getRear();  			// return 2
circularDeque.isFull();				// return true
circularDeque.deleteLast();			// return true
circularDeque.insertFront(4);			// return true
circularDeque.getFront();			// return 4


  • 풀이1_이중 연결 리스트를 이용한 데크 구현
class MyCircularDeque:

    def __init__(self, k: int):
        """
        Initialize your data structure here. Set the size of the deque to be k.
        """
        self.head, self.tail = ListNode(None), ListNode(None)
        # 최대 길이, 현재 길이
        self.k, self.len = k, 0
        self.head.right, self.tail.left = self.tail, self.head

        # 이중 연결 리스트에 신규 노드 삽입
        # 이미 있는 노드를 찢어내고 그 사이에 새로운 노드 new를 삽입하는 형태
    def _add(self, node: ListNode, new: ListNode):
        n = node.right
        node.right = new
        new.left, new.right = node, n
        n.left = new
    
    def _del(self, node:ListNode):
        n = node.right.right
        node.right = n
        n.left = node

    def insertFront(self, value: int) -> bool:
        """
        Adds an item at the front of Deque. Return true if the operation is successful.
        """
        # 이미 꽉 차있는 경우 False 반환
        if self.len == self.k:
            return False
        self.len += 1
        # head 위치에 노드 삽입
        self._add(self.head, ListNode(value))
        return True

    def insertLast(self, value: int) -> bool:
        """
        Adds an item at the rear of Deque. Return true if the operation is successful.
        """
        if self.len == self.k:
            return False
        self.len += 1
        # tail.left에 노드 삽입
        self._add(self.tail.left, ListNode(value))
        return True

    def deleteFront(self) -> bool:
        """
        Deletes an item from the front of Deque. Return true if the operation is successful.
        """
        if self.len == 0:
            return False
        self.len -= 1
        self._del(self.head)
        return True
        

    def deleteLast(self) -> bool:
        """
        Deletes an item from the rear of Deque. Return true if the operation is successful.
        """
        if self.len == 0:
            return False
        self.len -= 1
        self._del(self.tail.left.left)
        return True

    def getFront(self) -> int:
        """
        Get the front item from the deque.
        """
        return self.head.right.val if self.len else -1
        

    def getRear(self) -> int:
        """
        Get the last item from the deque.
        """
        return self.tail.left.val if self.len else -1

    def isEmpty(self) -> bool:
        """
        Checks whether the circular deque is empty or not.
        """
        return self.len == 0

    def isFull(self) -> bool:
        """
        Checks whether the circular deque is full or not.
        """
        return self.len == self.k

Result

Runtime : 108ms, Memory : 15.5MB


우선순위 큐

우선순위 큐는 큐 또는 스택과 같은 추상 자료형과 유사하지만 추가로 각 요소의 ‘우선순위’와 연관되어 있다.


문제02 k개 정렬 리스트 병합

https://leetcode.com/problems/merge-k-sorted-lists/

k개의 정렬된 리스트를 1개의 정렬된 리스트로 병합하라.

Example 1:

Input: lists = [[1,4,5],[1,3,4],[2,6]]
Output: [1,1,2,3,4,4,5,6]
Explanation: The linked-lists are:
[
  1->4->5,
  1->3->4,
  2->6
]
merging them into one sorted list:
1->1->2->3->4->4->5->6

Example 2:

Input: lists = []
Output: []

Example 3:

Input: lists = [[]]
Output: []


  • 풀이1_우선순위 큐를 이용한 리스트 병합
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def mergeKLists(self, lists: List[ListNode]) -> ListNode:
        root = result = ListNode(None)
        heap = []
        
        # 각 연결 리스트의 루트를 힙에 저장
        for i in range(len(lists)):
            if lists[i]:
                heapq.heappush(heap, (lists[i].val, i, lists[i]))
            
        # 힙 추출 이후 다음 노드는 다시 저장
        while heap:
            node = heapq.heappop(heap)
            idx = node[1]
            result.next = node[2]
            
            result = result.next
            if result.next:
                heapq.heappush(heap, (result.next.val, idx, result.next))
                
        return root.next

Result

Runtime : 96ms, Memory : 18MB


PriorityQueue vs heapq

파이썬의 우선순위 큐는 queue 모듈의 PriorityQueue클래스를 이용해 사용할 수 있다. 하지만 우선순위 큐는 주로 힙을 사용해 구현한다.

PriorityQueue와 heapq의 차이점은 무엇일까? 스레드 세이프(Thread-Safe)이다. 하지만 파이썬에서는 멀티 스레딩이 거의 의미가 없기 때문에 PriorityQueue를 사용할 필요가 없다.