2016/01/04: PriorityQueue in python

In python there is a class PriorityQueue. This class literally is what it says: you can add elements and take out the smallest. In particular, if you add an element twice, it sits in in the queue twice (so you get it twice).


~> python3
Python 3.4.3 (default, Jan  1 2016, 12:53:34) 
[GCC 4.2.1 20070831 patched [FreeBSD]] on freebsd9
Type "help", "copyright", "credits" or "license" for more information.
>>> from queue import PriorityQueue
>>> q = PriorityQueue()
>>> q.put(4)
>>> q.put(3)
>>> q.put(4)
>>> q.put(5)
>>> q.qsize()
4
>>> q.get()
3
>>> q.get()
4
>>> q.get()
4
>>> q.get()
5
>>> quit()
~>

While such a data structure is useful in general, it can have fun effects when used (e.g., when someone demonstrates how Dijkstra's algorithm works) in a place where you actually need a different data structure, one with the operations adding an element with a priority, updating the priority to a smaller value, and removing value with the smallest priority. Using a PriorityQueue with priority-value pairs almost works (hint: tuples are ordered lexicographically) except that you find, that nodes are visited more often than you expect. Of course, it is not hard to write a wrapper around PriorityQueue that provides this semantics.


from queue import PriorityQueue

class PQueue():
  """Queue which contains elements with a priority

  Supported operations are adding elements, updating
  the priority to a smaller value, and removing the
  element with the smallest priority.

  """
  def __init__(self):
    self.entries = {} # The actual entries
    self.q = PriorityQueue() # A priority queue of the entries,
                             # might, however, contain elements
                             # not in the PQueue

  def put(self, priokey):
    """Update the priority of an element, adding it if not yet present

    Take an (priority, element) pair and update the priority of the element,
    if the new value is smaller than the current value. If the element is
    not present in the queue, add it.

    """
    (prio, key) = priokey
    if key in self.entries:
      oldprio = self.entries[key]
      if prio < oldprio:
        self.entries[key] = prio
        self.q.put(priokey)
    else:
      self.entries[key] = prio
      self.q.put(priokey)

  def qsize(self):
    return (len(set(self.entries)))

  def empty(self):
    return self.qsize() == 0

  def get(self):
    """Remove the element with the smallest priority form the queue

    Return the pair of the priority and the element itself.

    """
    assert(not self.empty())
    while True:
      (prio, key) = self.q.get()
      if key in self.entries and self.entries[key] == prio:
        del self.entries[key]
        return (prio, key)
download