1

I am trying to program Dijkstra's algorithm for an adjacency list with user defined objects.

This project is part of my work, so I must keep things general.

I am using an adjacency list and a STL priority queue

Here is how I compare different nodes

class priorityQueueCompareNodes
{
    public:
    bool operator() ( const Node& leftSideNode, 
                  const Node& rightSideNode  )
    {
        return leftSideNode.cost > rightSideNode.cost;
    }
};

This is how I pop and insert into the priority queue.

//get the node with the lowest cost from the priority queue
Node closestNode = priorityQueue.top();

//pop this node from the queue
priorityQueue.pop();

When I relax edges and change the cost to different nodes I change the adjacency list, not the priority queue.

My question is how I can change the values in my priority queue so that I have an updated priority queue every time I relax edge weights and change distances/parents.

Thank you everyone.

Danial Esaid
  • 51
  • 1
  • 4
  • 1
    *This project is part of my work* -- Note that [boost](https://stackoverflow.com/questions/11726857/boosts-dijkstras-algorithm-tutorial) already implements Dijkstra's algorithm. – PaulMcKenzie Jul 09 '18 at 02:18
  • 1
    *How I can change the values in my priority queue so that I have an updated priority queue every time I relax edge weights and change distances/parents.* -- In other words, "How I can change the values in every time I change ". Is this what your question boils down to? If so, Observer Pattern, use `std::shared_ptr`, many ways to skin that cat, depending on your requirements. – PaulMcKenzie Jul 09 '18 at 02:20
  • 1
    One way would be use a std::set, delete the element and reinsert with the new key. This runs in O(log n). – ead Jul 09 '18 at 03:37
  • I don't know if this is good practice or not, but what I do is simply insert a new element into the priority queue when I find a shorter path to a node. The new entry will get processed first, and the old entry with the longer path will get ignored because the node has already been visited. – eesiraed Jul 09 '18 at 18:28
  • @FeiXiang it is possible to do that, but based on the specific type of graph one may be working on, the space usage of the priority queue may get quite large. – ilim Jul 09 '18 at 18:38

1 Answers1

0

Usage of any sort of pointer to access the inner workings of a std::priority_queue to update an element within is problematic. In a minimum priority queue, for instance, the data structure guarantees that the value of the node is smaller than that of its children. If you choose to update the value within the priority queue, you would have to do the bubble up operation manually in order to preserve the integrity of that guarantee. Doing bubble up/down operations manually sort of beats the point of using a collection like std::priority_queue.

Using a std::set offers a viable alternative. To retrieve the minimum and maximum, you can simply use the begin and rbegin pointers of the set. Aside from the set, you should keep an array/vector (i.e. random access collection) of minimum distances M for the nodes. Whenever your relaxation yields a shorter path to a node v, you may delete the old set entry of v, update M[v], and insert the entry of v to the set with the new M[v] value. Updating M[v] is done in constant time, and set operations take O(log|V|) time. So, even though it has a greater constant factor than priority queue, using a set has the same asymptotic complexity.

Below is a pseudocode illustrating the usage. Since your code was incomplete, I resorted to using standard datatypes. Vertices of the graph are 0-indexed, and for vi, the adjacency list adjList keeps a vector of outgoing edges in adjList[i]. For each edge, the index of the destination vertex and the weight of that edge are kept, respectively.

void dijkstra(vector< vector< pair<int, long long> > >& adjList, int source) {
    set< pair<long long, int> > priorityQueue;
    vector< long long > minCosts(adjList.size(), INFINITY);
    minCosts[source] = 0LL;
    priorityQueue.insert( make_pair(minCosts[source], source) );

    while(!priorityQueue.empty()) {
        set< pair<long long, int> >::iterator it = priorityQueue.begin();
        int u = it->second;
        long long costU = it->first;

        for(int i=0; i < adjList[u].size(); ++i) {
            int v = adjList[u][i].first;
            long long w = adjList[u][i].second;
            if (costU + w < minCosts[v]) {
                if(minCosts[v] < INFINITY) {
                    priorityQueue.erase( make_pair(minCosts[v], v) );
                }
                minCosts[v] = costU + w;
                priorityQueue.insert( make_pair(minCosts[v], v) );
            }
        }
    }

    // minCosts[] now keeps the minimal distance of all nodes from source
}

Obviously, the retrieval of edge costs and relevant adjacency list entries may be different in your codebase, but since you did not provide those details, I tried to provide a more generic code sample that could minimally emphasize the point. Basically, the set may just keep a pair of cost and node index, respectively, and you're all set.

Alternatively, you may define a comparison operator for vehicleNode type and just keep that inside the set, as opposed to a pair of cost & node. But that is just an implementation-specific decision, and not the main point of your question.

ilim
  • 4,477
  • 7
  • 27
  • 46
  • The problem is that two nodes may have the same distance from the source during the run of Dijkstra. This prevents using a `std::set`. On the other hand, using a `multi_set` will run you in trouble. If at a time node `v` and node `u` are at the same distance from the source, then `delete v` may indeed delete `u` from the `multi_set`.... – fjardon Jul 09 '18 at 12:20
  • Two nodes having equal distance from the source nodedoes not prevent using a set. Let me clarify my answer with a minimal code sample. – ilim Jul 09 '18 at 12:46
  • @fjardon I added a code sample illustrating a sample usage of `std::set` to simulate priority queue functionality. – ilim Jul 09 '18 at 18:46