更改优先级队列中项目的优先级

时间:2012-02-01 21:37:36

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

使用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的讨论)?

5 个答案:

答案 0 :(得分:4)

为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)

答案 1 :(得分:1)

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

答案 2 :(得分:0)

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

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

答案 3 :(得分:0)

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

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

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

答案 4 :(得分:0)

我已经实现了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
  }
}