Изменить приоритет элементов в очереди приоритетов - PullRequest
15 голосов
/ 02 февраля 2012

Использование Scala 2.9 для реализации своего рода алгоритма Дейкстры (псевдокод)

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

Я хотел бы изменить приоритет элемента в collection.mutable.PriorityQueue.

в Scala.на

  • удалить элемент
  • изменить приоритет
  • повторно вставить в очередь.

Но я не могу найти способ либо обновитьПриоритет или удаление определенного элемента (не обязательно элемента head), например java.util.PriorityQueue#remove(Object), как указано в Удаление элемента из очереди приоритетов .

  • Как эта задача может быть выполненасделано с scala.collection.mutable.PriorityQueue или мне нужно использовать java.util.PriorityQueue вместо этого?

  • Кто-нибудь знает, является ли отсутствие такого метода заданным, и было бы рекомендовано перестроить очередь послеизменение приоритета некоторых элементов (может быть, посмотрите обсуждение очереди приоритетов с динамическими приоритетами элементов )?

Ответы [ 5 ]

4 голосов
/ 02 февраля 2012

Определение класса case для типа PriorityQueue для использования с var для priority позволяет найти его и изменить приоритет.ПриоритетQueue тогда имеет это новое значение.Чтобы получить правильный порядок, я должен был клонировать его, который переупорядочивает / форсирует порядок.Возможно, есть лучший способ сделать это без клонирования.

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 голос
/ 02 февраля 2012

Приоритетные очереди обычно реализуются с кучами. Двоичные кучи обычно реализуются с использованием массивов , и если элемент, который вы хотите удалить, находится не на пути между корнем кучи и его последним элементом в порядке упорядочения массива, то очевидного способа удаления Это. Я предполагаю, что именно поэтому Scala не предлагает удаление произвольных элементов. Однако, если вы реализуете свою собственную кучу, достаточно просто реализовать ключ уменьшения для двоичной (минимальной) кучи: вы просто сравниваете новый приоритет для узла N с приоритетом его родителя и, если необходимо, меняете их. Повторяйте это до тех пор, пока N не окажется наверху или родительский элемент N не будет иметь более низкий приоритет, чем сам N.

0 голосов
/ 19 ноября 2018

Я реализовал класс , чтобы сделать именно то, что вам нужно:

  • вставка enqueue
  • выдержка в мин dequeue
  • Ключ уменьшения - 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
  }
}
0 голосов
/ 25 октября 2012

Не пользователь Scala, но до сих пор я никогда не видел встроенную / готовую реализацию Heap, которая позволяет уменьшить ключ, потому что уменьшение ключа эффективно только в том случае, если вы можете указать (местоположение) DK'd.

Самый простой способ получить операцию DK - это реализовать кучу самостоятельно. Мой метод, как правило, состоит в том, чтобы отделить мои элементы (в неорганизованном массиве / векторе / связанном списке) и построить кучу указателей на элементы (или массива-независимости). Затем, имея узел Heap, вы можете искать элемент, обращаясь к нему в массиве (разыменование или поиск по индексу). Для поддержки DK и / или произвольного удаления вы можете добавить дополнительную переменную к элементу, которая указывает на узел кучи (или сохраняет индекс, если кучи на основе массива). Это позволяет вам иметь доступ O (1) в любом направлении.

Если ваша предварительно созданная куча поставляется с DK-операцией, которая принимает указатель на узел, тогда вы можете просто создать кучу самодельных объектов узла, которые просто заключают указатель в класс, чтобы вы могли предоставить операторы сравнения. (требуется построить кучу из них).

0 голосов
/ 08 февраля 2012

У меня нет опыта работы со Scala, но проблема, с которой я сталкиваюсь, заключается в том, что очереди с простым приоритетом недостаточно для Дейкстры, так как вам нужно знать, где в очереди хранится определенная вершина, прежде чем вы сможете использовать клавишу уменьшения. Другими словами, словарь (хэш-таблица) необходим для отображения идентификаторов вершин на индексы в куче в ожидаемое постоянное время. Затем вы получаете общий O (log n) для клавиши уменьшения. Мне кажется маловероятным, что такую ​​функциональность можно найти в стандартной библиотеке. Написание подходящего класса с нуля должно быть легким.

Код в конце этой лекции объясняет идею (но в python ... извините).

...