之前介绍的 队列 是一种先进先出的线性数据结构。而优先级队列出队的时候是根据优先级判断的。
优先级队列有下面类型:
-
最大优先级,出队的时候永远是最大的先出队;
-
最小优先级,出队的时候永远是最小的先出队。
和二叉堆一样,优先级队列能够很好的在数据列表里面找到最大值和最小值,还有像一些最小花费计算,最短路径确定,堆排序等问题都可以使用优先级队列来解决。
实现
实现优先级队列的方式可以有:已排序数组,平衡二叉搜索树,二叉堆。
基于已排序数组数组实现的优先级队列,它的 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