I am working on an application that demonstrates the Djikstra's algorithm, and to use it, I need to restore the heap property when my elements' value is decreased.
The problem regarding the complexity is that when the algorithm changes the value of an element, that element's index in the internal structure (heap in this case) used for the priority queue is unknown. As such, I currently need to do an O(n) search, in order to recover the index, before I can perform an actual decrease-key on it.
Moreover, I am not exactly sure about the actual code needed for the operation. I am using the D-Heap here for my Priority Queue. Pseudocode would help, but I would prefer an example in Java on how this should be done.
You can do following: store a hashmap inside your heap that maps your heap values to heap indexes. Then you should extend your usual heap-logic just a bit:
BubbleUp(index)
in case ofDecreaseKey
, andBubbleDown/Heapify(index)
in case ofIncreaseKey
, to restore min-heap-property.Here's my C# implementation: http://pastebin.com/kkZn123m
Insert and ExtractMin call Swap log(N) times, when restoring heap property. And you're adding O(1) overhead to Swap, so both operations remain O(log(n)). UpdateKey is also log(N): first you lookup index in a hashmap for O(1), then you're restoring heap property for O(log(N)) as you do in Insert/ExtractMin.
Important note: using values for index lookup will require that they are UNIQUE. If you're not ok with this condition, you will have to add some uniqueue id to your key-value pairs and maintain mapping between this uniqueue id and heap index instead of value-index mapping. But for Dijkstra it's not needed, as your values will be graph nodes and you don't want duplicate nodes in your priority queue.
If you are using c++ stl make_heap()/pop_heap()/push_heap(), there is no way to keep an index from node id to index in the underline heap vector, I think you should implement your own heap functions to achieve O(logn) in Increase-Key/Decrease-key operation.
Per this SO question it is unnecessary to have a decrease-key method in order to implement Dijkstra's Algorithm.
You can simply add an item to the priority queue as many times as necessary and keep track of which nodes you have visited to weed out the duplicates. The first time you actually visit a node by popping it off of the queue, you have found the shortest path to that node and can ignore all future occurrences of it on the priority queue.
Having many extra nodes on the priority queue is not much of a problem because it is an
O(log N)
structure. (You have to do about 20 comparisons for 1 million items and 30 comparison for 1 billion items.)Edit: Following up on this question later, I'm a bit disappointed by my answer: all of those things will have to come off of the queue unless you do some special canipulations later. Like many things in life, it comes down to how you manage your memory and the costs associated with doing so. But the general point remains: decrease-key is not necessary, even if it might be desirable.