Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java Priority Queue reordering when editing elements

I'm trying to implement Dijkstra's algorithm for finding shortest paths using a priority queue. In each step of the algorithm, I remove the vertex with the shortest distance from the priority queue, and then update the distances for each of its neighbors in the priority queue. Now I read that a Priority Queue in Java won't reorder when you edit the elements in it (the elements that determine the ordering), so I tried to force it to reorder by inserting and removing a dummy vertex. But this doesn't seem to be working, and I'm stuck trying to figure it out.

This is the code for the vertex object and the comparator

class vertex {     int v, d;     public vertex(int num, int dis) {         v=num;         d=dis;     } }  class VertexComparator implements Comparator {     public int compare (Object a, Object b) {         vertex v1 = (vertex)a;         vertex v2 = (vertex)b;         return v1.d-v2.d;     }  } 

Here is then where I run the algorithm:

    int[] distances=new int[p];     Comparator<vertex> comparator = new VertexComparator();     PriorityQueue<vertex> queue = new PriorityQueue<vertex>(p, comparator);     for(int i=0; i<p; i++) {         if(i!=v) {             distances[i]=MAX;         }         else {             distances[i]=0;         }         queue.add(new vertex(i, distances[i]));     }     // run dijkstra     for(int i=0; i<p; i++) {         vertex cur=queue.poll();         Iterator itr = queue.iterator();         while(itr.hasNext()) {             vertex test = (vertex)(itr.next());             if(graph[cur.v][test.v]!=-1) {                 test.d=Math.min(test.d, cur.d+graph[cur.v][test.v]);                 distances[test.v]=test.d;             }         }         // force the PQ to resort by adding and then removing a dummy vertex         vertex resort = new vertex(-1, -1);         queue.add(resort);         queue.remove(resort);     } 

I've run several text cases, and I know that the priority queue isn't reordering correctly each time I go through and update the distances for vertices, but I don't know why. Did I make an error somewhere?

like image 551
novice Avatar asked Aug 05 '11 07:08

novice


People also ask

Does priority queue sort the elements?

This priority queue will be sorted according to the same comparator as the given collection, or according to its elements' natural order if the collection is sorted according to its elements' natural order. Parameters: c - the collection whose elements are to be placed into this priority queue.

How do you update a priority queue element?

To update priority in Priority Queue, get the index of the element that you want to update the priority of and assign a new key to the element. After updating the priority of an element, we need to heapify the heap to maintain the heap data structure.

Is priority queue always sorted?

A PriorityQueue is what is called a binary heap. It is only ordered/sorted in the sense that the first element is the least. In other word, it only cares about what is in the front of the queue, the rest are "ordered" when needed.

Does priority queue sort in ascending order?

In a priority queue, items are ordered alphabetically in ascending order, numerically in ascending order, or according to a user-defined attribute based on a custom comparator.


2 Answers

As you discovered, a priority queue does not resort all elements whenever an element is added or removed. Doing that would be too expensive (remember the n log n lower bound for comparison sort), while any reasonable priority queue implementation (including PriorityQueue) promises to add/remove nodes in O(log n).

In fact, it doesn't sort its elements at all (that's why its iterator can not promise to iterate elements in sorted order).

PriorityQueue does not offer an api to inform it about a changed node, as that would require it to provide efficient node lookup, which its underlying algorithm does not support. Implementing a priority queue that does is quite involved. The Wikipedia article on PriorityQueues might be a good starting point for reading about that. I am not certain such an implementation would be faster, though.

A straightforward idea is to remove and then add the changed node. Do not do that as remove() takes O(n). Instead, insert another entry for the same node into the PriorityQueue, and ignore duplicates when polling the queue, i.e. do something like:

PriorityQueue<Step> queue = new PriorityQueue();  void findShortestPath(Node start) {     start.distance = 0;     queue.addAll(start.steps());      Step step;     while ((step = queue.poll()) != null) {         Node node = step.target;         if (!node.reached) {             node.reached = true;             node.distance = step.distance;             queue.addAll(node.steps());         }     }  } 

Edit: It is not advisable to change the priorities of elements in the PQ, hence the need to insert Steps instead of Nodes.

like image 101
meriton Avatar answered Oct 01 '22 04:10

meriton


you will have to delete and re-insert each element which is editted. (the actual element, and not a dummy one!). so, every time you update distances, you need to remove and add the elements that were affected by the changed entree.

as far as I know, this is not unique to Java, but every priority queue which runs at O(logn) for all ops, have to work this way.

like image 40
amit Avatar answered Oct 01 '22 04:10

amit