Priority Queue 생성.

생성 하기 전에 어떤 방식을 사용하면 Priority Queue를 만들수 있을까?

  1. 정렬 된 배열
  2. Balanced Binary Search Tree
  3. Heap

그래서 Heap을 사용해서 Priority Queue를 생성해야 합니다.

// Queue Create
public protocol Queue {
  associatedtype Element
  mutating func enqueue(_ element: Element) -> Bool
  mutating func dequeue() -> Element?
  var isEmpty: Bool { get }
  var peek: Element? { get }
}

// Heap Create
public struct Heap<Element: Equatable> {
  
  var elements: [Element] = []
  let sort: (Element, Element) -> Bool
  
  public init(sort: @escaping (Element, Element) -> Bool, elements: [Element] = []) {
    self.sort = sort
    self.elements = elements
    
    if !elements.isEmpty {
      for i in stride(from: elements.count / 2 - 1, through: 0, by: -1) {
        siftDown(from: i)
      }
    }
  }
  
  public var isEmpty: Bool {
    elements.isEmpty
  }
  
  public var count: Int {
    elements.count
  }
  
  public func peek() -> Element? {
    elements.first
  }
  
  func leftChildIndex(ofParentAt index: Int) -> Int {
    (2 * index) + 1
  }
  
  func rightChildIndex(ofParentAt index: Int) -> Int {
    (2 * index) + 2
  }
  
  func parentIndex(ofChildAt index: Int) -> Int {
    (index - 1) / 2
  }
  
  public mutating func remove() -> Element? {
    guard !isEmpty else {
      return nil
    }
    elements.swapAt(0, count - 1)
    defer {
      siftDown(from: 0)
    }
    return elements.removeLast()
  }
  
  mutating func siftDown(from index: Int) {
    var parent = index
    while true {
      let left = leftChildIndex(ofParentAt: parent)
      let right = rightChildIndex(ofParentAt: parent)
      var candidate = parent
      if left < count && sort(elements[left], elements[candidate]) {
        candidate = left
      }
      if right < count && sort(elements[right], elements[candidate]) {
        candidate = right
      }
      if candidate == parent {
        return
      }
      elements.swapAt(parent, candidate)
      parent = candidate
    }
  }
  
  public mutating func insert(_ element: Element) {
    elements.append(element)
    siftUp(from: elements.count - 1)
  }
  
  mutating func siftUp(from index: Int) {
    var child = index
    var parent = parentIndex(ofChildAt: child)
    while child > 0 && sort(elements[child], elements[parent]) {
      elements.swapAt(child, parent)
      child = parent
      parent = parentIndex(ofChildAt: child)
    }
  }
  
  mutating func remove(at index: Int) -> Element? {
    guard index < elements.count else {
      return nil
    }
    if index == elements.count - 1 {
      return elements.removeLast()
    } else {
      elements.swapAt(index, elements.count - 1)
      defer {
        siftDown(from: index)
        siftUp(from: index)
      }
      return elements.removeLast()
    }
  }
  
  func index(of element: Element, startingAt i: Int) -> Int? {
    if i >= count {
      return nil
    }
    if sort(element, elements[i]) {
      return nil
    }
    if element == elements[i] {
      return i
    }
    if let j = index(of: element, startingAt: leftChildIndex(ofParentAt: i)) {
      return j
    }
    if let j = index(of: element, startingAt: rightChildIndex(ofParentAt: i)) {
      return j
    }
    return nil
  }
}
struct PriorityQueue<Element: Equatable>: Queue { // 1

	private var heap: Heap<Element> // 2

	
	init(sort: @escaping (Element, Element) -> Bool, elements: [Element] = []) { // 3
		heap = Heap(sort: sort, elements: elements)
	}

	var isEmpty: Bool {
		heap.isEmpty
	}
	
	var peek: Element? {
		heap.peek()
	}
	
	mutating func enqueue(_ element: Element) -> Bool { // 4
		heap.insert(element)
		return true
	}
	
	mutating func dequeue() -> Element? { // 5
		heap.remove()
	}
}
  1. PriorityQueue는 Queue Protocol을 준수합니다. Element는 비교가 가능해야 하므로 Equatable을 준수해야 합니다.
  2. 이 Heap을 사용해서 priority queue를 구현합니다.
  3. 이 initializer에 적절한 function을 전달해서 PriorityQueue를 사용하여 최소 및 최대 priority queue를 모두 작성 할 수 있습니다.
  4. enqueue(:)를 호출하면 Heap에 삽입 되면서 vaildate itself(유효성 검사)를 하기 위해 sift up을 합니다. enqueue(:)의 전체 복잡도는 O(log n)입니다.
  5. dequeue(_:)를 호출하면 Heap의 root element를 heap의 마지막 요소와 바꾸고 heap에서 제거하고 sift down을 합니다. dequeue의 전체적인 복잡성은 O(log n)입니다.

Priority Queue 테스트.