【问题标题】:Change priority of items in a priority queue更改优先级队列中项目的优先级
【发布时间】:2012-02-24 14:24:52
【问题描述】:

使用Scala 2.9实现一种Dijkstra算法(伪代码)

val queue = new PriorityQueue
queue.insert(...)
while (!queue.isEmpty) {
  val u = queue.extractMin
  queue.foreach { v =>
    if (condition(u, v))
      queue.decreaseKey(v, newPriority)
  }
}

我想在 Scala 的 collection.mutable.PriorityQueue 中更改项目的优先级。

因此尝试

  • 删除项目
  • 更改优先级
  • 重新插入队列。

但我找不到更新优先级或删除特定项目的方法(不是 必须是头元素),如java.util.PriorityQueue#remove(Object) Removing an item from a priority queue.

  • 如何使用scala.collection.mutable.PriorityQueue 完成此任务 还是我必须改用java.util.PriorityQueue

  • 有谁知道缺少这种方法是否是设计使然,建议使用 在更改某些项目的优先级后重建队列(也许看看关于Priority queue with dynamic item priorities的讨论)?

【问题讨论】:

  • 我不是 Scala 用户,但我知道 java.util.PriorityQueue 不支持 reduce-key。我相信这是因为为了有效地支持 reduce-key,你需要能够随机访问 PriorityQueue 的元素,这是 Java 版本不支持的。
  • 顺便说一句,您确定 PriorityQueue 是解决您的问题的正确方法吗,因为我看到您没有直接使用它,并且队列上的 AFAIK 随机访问操作将是 O(n) ?
  • @om-nom-nom:你知道类似 Dijkstra 算法(使用 insert、extractMin 和 reductionKey)中 PriorityQueue 的替代品吗?而且我读到,当外部存储元素的插入位置时,随机访问甚至可能是 O(1) - 但恐怕这不是标准 impl 实现的。
  • PriorityQueue 将完全适合 extractMin-decrease-enqueueBack,我被 不一定是 head 元素搞砸了
  • @om-nom-nom:在每个 extractMin 步骤之后,可能会有一些减少键(这意味着一些(因此不是头部)元素优先级值更改(减少))。

标签: algorithm scala data-structures heap priority-queue


【解决方案1】:

为 PriorityQueue 类型定义一个案例类以与 var 一起使用作为优先级,这样您就可以找到它并改变优先级。 PriorityQueue 然后具有这个新值。为了得到正确的排序,我必须克隆它来重新排序/强制排序。没有克隆可能有更好的方法。

case class Elem(var priority: Int, i: Int)

def MyOrdering = new Ordering[Elem] {
  def compare(a : Elem, b : Elem) = a.priority.compare(b.priority)
}

val pq = new scala.collection.mutable.PriorityQueue[Elem]()(MyOrdering)  ++ List(Elem(1,1), Elem(0,0), Elem(2,2))

pq.find(x => x.priority == 0) match {
  case Some(elem: Elem) => elem.priority = 3
  case None => println("Not found")
}

val pq2 = pq.clone
println(pq2)
println(pq2.dequeue)
println(pq2.dequeue)
println(pq2.dequeue)



:load SO.scala
Loading SO.scala...
defined class Elem
PileOrdering: java.lang.Object with Ordering[Elem]
pq: scala.collection.mutable.PriorityQueue[Elem] = PriorityQueue(Elem(2,2), Elem(0,0), Elem(1,1))
pq2: scala.collection.mutable.PriorityQueue[Elem] = PriorityQueue(Elem(3,0), Elem(2,2), Elem(1,1))
PriorityQueue(Elem(3,0), Elem(2,2), Elem(1,1))
Elem(3,0)
Elem(2,2)
Elem(1,1)

【讨论】:

  • 实际上您可以像这样将排序绑定到案例类:case class Elem(var priority: Int, i: Int) extends Ordering[Elem] { def compare(that : Elem) = that.priority.compare(priority) } 然后只需使用new PriorityQueue[Elem](Elem(1,1), Elem(0,0), Elem(2,2))
  • @Brian:如果我理解正确,您建议在更改某些元素的优先级后再次建立队列(重新排序)。
  • @binuWADa 好吧,我不建议这样做,因为它效率低下,但克隆确实会重新排序更改后的队列。我怀疑可能有更好的方法可以在不克隆队列的情况下完成此操作。如果您在优先级更改后在 pq 上出列 x 3,则排序不正确。
【解决方案2】:

优先级队列通常使用堆来实现。二进制堆是commonly implemented using arrays,如果要删除的元素不在堆的根和它在数组排序中的最后一个元素之间的路径上,那么没有明显的方法可以删除它。我认为这就是 Scala 不提供删除任意元素的原因。但是,如果您实现自己的堆,则很容易为二进制(最小)堆实现 reduce-key:您只需将节点 N 的新优先级与其父节点的优先级进行比较,并在必要时交换两者。重复执行此操作,直到 N 位于顶部或 N 的父级的优先级低于 N 本身。

【讨论】:

    【解决方案3】:

    我没有使用 Scala 的经验,但我看到的问题是,对于 Dijkstra 来说,简单的优先级队列是不够的,因为在执行减少键之前,您需要知道特定顶点在队列中的存储位置。换句话说,需要字典(哈希表)在预期的恒定时间内将顶点 ID 映射到堆内的索引。然后你得到减少键的总体 O(log n)。在我看来,这样的功能不太可能在标准库中找到。不过,从头开始编写一个合适的类应该很容易。

    this lecture 末尾的代码解释了这个想法(但在 python 中......对不起)。

    【讨论】:

      【解决方案4】:

      不是 Scala 用户,但到目前为止,我从未见过允许 Decrease Key 的内置/预制堆实现,因为 Decrease Key 只有在您可以提供元素(的位置)时才有效DK了。

      获取 DK 操作的最简单方法是自己实现 Heap。我的方法通常是将我的元素分开(在一个无组织的数组/向量/链表中)并构建一个指向元素(或数组索引)的指针堆。然后,给定一个堆节点,您可以通过在数组中访问它来查找元素(取消引用或索引查找)。要支持 DK 和/或随机删除,您可以向指向堆节点的元素添加一个附加变量(或保留索引,如果基于数组的堆)。这使您可以在任一方向上进行 O(1) 访问。

      如果您的预制堆带有一个接受指向节点的指针的 DK 操作,那么您可以简单地构建一个自制节点对象的堆,它只需将指针包装在一个类中,以便您可以提供比较运算符(需要构建它们的堆)。

      【讨论】:

        【解决方案5】:

        我已经实现了 class 来完全满足您的需求:

        • 插入是enqueue
        • extractMin 是dequeue
        • decreaseKey 是putValue

        import scala.annotation.tailrec
        import scala.collection.mutable.{ArrayBuffer, Map}
        import scala.util.Try
        
        class HeapedMap[K, V] (implicit ord: Ordering[V]){
          val dict = Map[K,Int]() // Keeps index of key withing vector
          val vector = ArrayBuffer[(K,V)]()
        
          override def toString(): String = vector.toString
          def toMap(): scala.collection.immutable.Map[K,V] = dict.mapValues(vector(_)._2).toMap
          def toSeq(): Seq[(K,V)] = vector
          def toList(): List[(K,V)] = vector.toList
        
          private def parent(i: Int): Int = (i - 1) / 2
          private def right(i: Int): Int = (i * 2 + 1) + 1
          private def left(i: Int): Int = (i * 2 + 1)
          private def exists(i: Int): Boolean = Try(vector(i)).isSuccess
          private def compare(x: V, y: V): Boolean = ord.lteq(x,y)
          private def swap(i: Int, j: Int): Unit = {
            val aux = vector(i)
            vector(i) = vector(j)
            dict(vector(i)._1) = i
            vector(j) = aux
            dict(vector(j)._1) = j
          }
          private def replace(i: Int, j: Int): Unit = {
            dict -= vector(i)._1
            vector(i) = vector(j)
            dict(vector(i)._1) = i
            vector.remove(j)
          }
        
          private def insert(key: K, value: V): Unit = {
            vector += ((key, value))
            dict(key) = vector.size - 1
            bubbleUp(vector.size - 1)
          }
        
          private def delete(i: Int): Unit = {
            if (vector.size == 1) {
              dict -= vector(0)._1
              vector.remove(0)
            } else {
              replace(i, vector.size - 1)
              bubbleDown(i)
            }
          }
        
          def isEmpty(): Boolean = vector.isEmpty
        
          def enqueue(key: K, value: V): Unit = {
            if (!dict.contains(key)) {
              insert(key,value)
            }
            // TODO: handle when it already contains the key
          }
        
          @tailrec
          private def bubbleUp(i: Int): Unit = {
            val p = parent(i)
            if ((p != i) && (!compare(vector(p)._2, vector(i)._2))) {
              swap(p, i)
              bubbleUp(p)
            }
          }
        
          @tailrec
          private def bubbleDown(i: Int): Unit = {
            var largest = i
            val l = left(i)
            val r = right(i)
            if ((exists(l)) && (compare(vector(l)._2, vector(largest)._2))) {
              largest = l
            }
            if ((exists(r)) && (compare(vector(r)._2, vector(largest)._2))) {
              largest = r
            }
        
            if (largest != i) {
              swap(i, largest)
              bubbleDown(largest)
            }
          }
        
          def dequeue(): Option[(K, V)] = {
            val optionRoot = vector.headOption
            if (optionRoot.isDefined) {
                delete(0)
            }
            optionRoot
          }
        
          def dequeueAll(): Seq[(K,V)] = {
            val resp = ArrayBuffer[(K,V)]()
            var x = dequeue
            while (x.isDefined) {
              resp += x.get
              x = dequeue
            }
            resp
          }
        
          def headOption(): Option[(K,V)] = vector.headOption
          def get(k: K): Option[V] = {
            dict.get(k) match {
              case Some(i) => Some(vector(i)._2)
              case None => None
            }
          }
        
          // change values and heapify
          // * PriorityQueue does not have this feature
          def putValue(key: K, value: V): Unit = {
            val optionOldValue = get(key)
            if (optionOldValue.isDefined) {
              val oldValue = optionOldValue.get
              val i = dict(key)
              vector(i) = (key, value)
              if (compare(value, oldValue)) {
                bubbleUp(i)
              } else {
                bubbleDown(i)
              }
            } else {
              // if key does not exist, insert it
              insert(key,value)
            }
          }
        
          // different from dequeue, removes an arbitrary element
          def remove(key: K): Unit = {
            if (dict.contains(key)) {
              delete(dict(key))
            }
            // TODO: handle when it does not contain the key
          }
        }
        

        【讨论】:

          猜你喜欢
          • 2012-03-03
          • 2011-01-18
          • 2011-12-20
          • 1970-01-01
          • 2013-02-13
          • 1970-01-01
          • 2010-10-01
          • 1970-01-01
          相关资源
          最近更新 更多