数据结构学习-优先级队列(Priority Queue)

1,088 阅读2分钟

之前介绍的 队列 是一种先进先出的线性数据结构。而优先级队列出队的时候是根据优先级判断的。

优先级队列有下面类型:

  • 最大优先级,出队的时候永远是最大的先出队;

  • 最小优先级,出队的时候永远是最小的先出队。

和二叉堆一样,优先级队列能够很好的在数据列表里面找到最大值和最小值,还有像一些最小花费计算,最短路径确定,堆排序等问题都可以使用优先级队列来解决。

实现

实现优先级队列的方式可以有:已排序数组,平衡二叉搜索树,二叉堆。

基于已排序数组数组实现的优先级队列,它的 enqueue 和 dequeue 操作时间复杂度是 O(n),主要是因为数组分配的内存空间是连续的,所以删除和插入操作需要进行位置移动;

基于平衡二叉搜索树实现的优先级队列,它的 enqueue 和 dequeue 操作时间复杂度是 O(log n),这个其实有点类似已排序数字数组,里面的数据都是排好序的;

基于二叉堆的实现的优先级队列,它的 enqueue 和 dequeue 操作时间复杂度是 O(log n),排序规则没有平衡二叉搜索树那么严格;

下面使用基于 二叉堆 形式实现优先级队列。队列需要实现的协议有:

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

里面包含了一个队列使用的基本功能。

优先级队列的实现:

struct PriorityQueue<Element: Equatable>: Queue {
    
    // 底部数据存储使用二叉堆
    private var heap: Heap<Element>
    
    init(sort: @escaping (Element, Element) -> Bool, elements: [Element] = []) {
        heap = Heap(sort: sort, elements: elements)
    }
    
    var isEmpty: Bool {
        heap.isEmpty
    }
    
    var peek: Element? {
        heap.peek()
    }
    
    // 时间复杂度 O(log n)
    mutating func enqueue(_ element: Element) -> Bool {
        heap.insert(element)
        return true
    }
    
    // 时间复杂度 O(log n)
    mutating func dequeue() -> Element? {
        heap.remove()
    }
}

extension PriorityQueue: CustomStringConvertible {
    
    var description: String {
        heap.elements.description
    }
}

由于之前已经实现了二叉堆,所以在二叉堆的基础实现队列协议的代码其实很简单。

测试

下面是一个最大优先级的优先级队列测试(最大值会在先出队):

    //  sort: >
    var queue = PriorityQueue(sort: >, elements: [1, 23, 34, 3, 43, 32, 3, 44, 442])
    print(queue)
    
    print("\n开始 dequeue 出队:\n")
    
    while !queue.isEmpty {
        print(queue.dequeue()!)
    }

输出:

[442, 44, 34, 23, 43, 32, 3, 1, 3]

开始 dequeue 出队:

442
44
43
34
32
23
3
3
1

最小优先级的优先级队列是这样的(最小值会先出队):

   // sort: <,
    var queue = PriorityQueue(sort: <, elements: [1, 23, 34, 3, 43, 32, 3, 44, 442])
    print(queue)
    
    print("\n开始 dequeue 出队:\n")
    
    while !queue.isEmpty {
        print(queue.dequeue()!)
    }

输出:

[1, 3, 3, 23, 43, 32, 34, 44, 442]

开始 dequeue 出队:

1
3
3
23
32
34
43
44
442