【scala原理系列】PriorityQueue原理用法示例源码详解
源自专栏《SparkML:scala原理系列目录》
文章目录
概览
这个类使用堆实现了优先队列。在创建时,必须提供一个隐式的 Ordering[A] 对象来对元素进行排序。
如果在 PriorityQueue 的排序中有多个元素具有相同的优先级,那么不能保证 dequeue 或 dequeueAll 方法返回元素的顺序。
特别地,这个类不保证按先进先出的方式返回元素,尽管从类名 Queue 可以错误地推断出这种行为。
只有 dequeue 和 dequeueAll 方法会按照优先顺序(同时从堆中删除元素)返回元素。标准集合方法,如 drop、iterator 和 toString,将按照最方便的顺序删除或遍历堆。
因此,打印一个 PriorityQueue 不会显示元素的优先顺序,虽然优先级最高的元素将首先被打印出来。要按顺序打印元素,必须复制 PriorityQueue(例如使用 clone 方法),然后逐个出队:
@example {
{
{
val pq = collection.mutable.PriorityQueue(1, 2, 5, 3, 7)
println(pq) // 元素可能不是按顺序的
println(pq.clone.dequeueAll) // 打印 Vector(7, 5, 3, 2, 1)
}}}
原理
PriorityQueue
(优先队列)是一种数据结构,它是基于堆(Heap)实现的。堆是一种完全二叉树,其中每个节点的值都大于等于其子节点的值(最大堆),或者每个节点的值都小于等于其子节点的值(最小堆)。
PriorityQueue
的原理是通过维护一个堆来保持元素的有序性。在 PriorityQueue
中,每个元素都具有优先级,并且较高优先级的元素会被先处理。内部实现使用数组来存储元素,根据元素的优先级进行堆调整(上浮和下沉操作),以确保堆的性质得到保持。
在 PriorityQueue
中,插入元素时,新元素被放置在数组的末尾,并根据其优先级进行上浮操作,将其移动到正确的位置以满足堆的性质。删除元素时,位于数组开头的元素(即根节点)具有最高优先级,因此被移除,并用数组末尾的元素替换。然后,根据其优先级进行下沉操作,将新元素移到正确的位置以满足堆的性质。这样就保证了优先队列中始终可以快速访问和删除具有最高优先级的元素。
通过使用堆数据结构,PriorityQueue
可以在插入和删除操作中具有较高的效率。插入元素的时间复杂度为O(log n),其中n是队列中的元素数量。删除最高优先级元素的时间复杂度也为O(log n)。由于堆的性质,访问具有最高优先级的元素的时间复杂度为O(1)。
因此,PriorityQueue
提供了一种有效地处理具有优先级的元素的方式,并保持它们的有序性。这在许多应用中非常有用,例如任务调度、事件处理等场景。
方法总结
方法名 | 描述 |
---|---|
toQueue |
将优先队列转换为普通队列。 |
self |
返回代理类中的原始优先队列实例。 |
self = PriorityQueueProxy.this.self.clone() |
克隆代理类中的原始优先队列实例。 |
reverseIterator |
返回一个以与正常顺序相反的顺序返回所有元素的迭代器。 |
reverse |
返回优先队列的反转版本。新的优先队列具有与原始队列相同的元素,但是顺序相反。 |
result(): PriorityQueue[A] |
返回一个已经进行堆调整的优先队列。 |
result |
返回该构建器的结果(优先队列本身)。 |
p_swap(a: Int, b: Int) |
交换数组中的两个位置的元素。 |
p_size0_=(s: Int) |
设置数组的大小。 |
p_size0 |
获取数组的大小。 |
p_ensureSize(n: Int) |
确保数组的大小至少为n。 |
p_array |
获取数组的引用。 |
next(): A |
返回迭代器的下一个元素。 |
newBuilder |
创建一个新的构建器,用于构建优先队列。 |
length |
返回优先队列中的元素数量。 |
hasNext |
检查迭代器是否还有下一个元素。 |
enqueue(elems: A*) |
将多个元素添加到队列中。 |
dequeueAll |
返回一个包含队列中所有元素的新集合。 |
dequeue() |
返回并删除队列中的最高优先级元素。 |
clear() |
清空队列,使其为空。 |
+= |
将单个元素插入队列。 |
++ |
将另一个可遍历对象中的所有元素添加到队列。 |
示例代码
package com.test
import scala.collection.mutable.PriorityQueue
object PriorityQueueDemo {
def main(args: Array[String]): Unit = {
// 创建一个优先队列,并指定元素的排序方式(按照整数大小降序排列)
val pq = PriorityQueue[Int]()(Ordering.Int.reverse)
// 添加元素到优先队列
pq += 5
pq += 2
pq += 8
// 将可遍历对象中的所有元素添加到优先队列
pq ++= Seq(10, 3, 1)
// 打印队列中的所有元素
println(" Priority Queue: " + pq.toList)
// 有序打印队列中的所有元素
println("Sorted Priority Queue: " + pq.clone().dequeueAll)
// 弹出最高优先级的元素并打印
val highestPriorityElement = pq.dequeue()
println("Highest Priority Element: " + highestPriorityElement)
// 返回队列中最高优先级的元素(最小堆)但不移除
val peekedElement = pq.head
println("Peeked Element: " + peekedElement)
// 清空队列
pq.clear()
// 检查队列是否为空
val isEmpty = pq.isEmpty
println("Is Queue Empty? " + isEmpty)
}
}
//Priority Queue: List(1, 3, 2, 10, 5, 8)
//Sorted Priority Queue: Vector(1, 2, 3, 5, 8, 10)
//Highest Priority Element: 1
//Peeked Element: 2
//Is Queue Empty? true
中文源码分析
import generic._
/**
* 这个类使用堆实现了优先队列。
* 在创建时,必须提供一个隐式的 Ordering[A] 对象来对元素进行排序。
*
* 如果在 PriorityQueue 的排序中有多个元素具有相同的优先级,
* 那么不能保证 dequeue 或 dequeueAll 方法返回元素的顺序。
* 特别地,这个类不保证按先进先出的方式返回元素,尽管从类名 Queue 可以错误地推断出这种行为。
*
* 只有 dequeue 和 dequeueAll 方法会按照优先顺序(同时从堆中删除元素)返回元素。
* 标准集合方法,如 drop、iterator 和 toString,将按照最方便的顺序删除或遍历堆。
*
* 因此,打印一个 PriorityQueue 不会显示元素的优先顺序,
* 虽然优先级最高的元素将首先被打印出来。
* 要按顺序打印元素,必须复制 PriorityQueue(例如使用 clone 方法),然后逐个出队:
*
* @example {
{
{
* val pq = collection.mutable.PriorityQueue(1, 2, 5, 3, 7)
* println(pq) // 元素可能不是按顺序的
* println(pq.clone.dequeueAll) // 打印 Vector(7, 5, 3, 2, 1)
* }}}
*
* @tparam A PriorityQueue 中元素的类型
* @param ord 隐式的 Ordering[A] 对象,用于比较元素的顺序。
*
* @author Matthias Zenger
* @since 1
*
* @define Coll PriorityQueue
* @define coll priority queue
* @define orderDependent
* @define orderDependentFold
* @define mayNotTerminateInf
* @define willNotTerminateInf
*/
@SerialVersionUID(736425014438295802L)
sealed class PriorityQueue[A](implicit val ord: Ordering[A])
extends AbstractIterable[A]
with Iterable[A]
with GenericOrderedTraversableTemplate[A, PriorityQueue]
with IterableLike[A, PriorityQueue[A]]
with Growable[A]
with Builder[A, PriorityQueue[A]]
with Serializable
with scala.Cloneable
{
import ord._
@SerialVersionUID(3491656538574147683L)
private class ResizableArrayAccess[A] extends AbstractSeq[A] with ResizableArray[A] with Serializable {
def p_size0 = size0
def p_size0_=(s: Int) = size0 = s
def p_array = array
def p_ensureSize(n: Int) = super.ensureSize(n)
def p_swap(a: Int, b: Int) = super.swap(a, b)
}
protected[this] override