简体   繁体   中英

Most efficient implementation of Dijkstra's shortest path using vectors and pairs in C++ stl

If std::vector<vector<pair<int,int> > > v(n) represents the adjacency list of the graph with pair<int,int> is the {vertex,weight} pair, I tried to implement the algorithm the following way:

while (true)
{
    long long yo = LLONG_MAX;
    int ind = -1;
    for (int i = 0; i < n; ++i)
    {
        if (ans[i] < yo && !v[i].empty())
        {
            ind = i;
            yo = ans[i];
        }
    }
    if (ind == -1)
        break;
    for (int i = 0; i < v[ind].size(); ++i)
    {
        if (ans[v[ind][i].first] > ans[ind] + v[ind][i].second)
            ans[v[ind][i].first] = ans[ind] + v[ind][i].second;
        v[ind].erase(v[ind].begin() + i);
    }
}

Where ans[i] stores the shortest paths which is initialised as {LLONG_MAX,...0,...LLONG_MAX} , 0 being the source. Since this is the first time I tried implementing it, is there a better way to implement using the vectors/list in stl (in terms of time/space complexity maybe)?

Current approach has logical bug ( modifying the vector while iterating it ). Also Complexity wise it seems very bad as well, due to redundant while loop to get the new minimum distance node each time and un-necessary erase operation, which is heavy in vector due to shifting of the entire adjacency list.

I would recommend using a priority queue < min heap >, which is much cleaner and have a complexity of Elog(N), where E is no. of edges and N no. of node in graph. I am just copying pasting one of my old codes with comments.

#define P first
#define Q second 
typedef long long LL ;
typedef pair < LL , int > li ;
typedef pair < int , LL > il ;
dist[N] = {INT_MAX} ;  // array containing the distance to each node from source node 
vector < il > adj [100010] ; // adjacency list with (node, distance) pair 

void dijkstra( int s ){   // s is the source node
  priority_queue < li , vector < li > , greater < li > > pq; // priortiy queue with a pair <long, int> as the data , using vector as underlying data structure and std:greater comparator 
  dist [s] = 0;
  pq.push( li( dist[s], s) );
  while( !pq.empty() ){
      li u = pq.top() ; pq.pop() ; // u.P =  current minimum distance of node , u.Q = current node on top of the heap 
      if( dist[u.Q] == u.P )
      for( int i = 0 ; i  < adj[u.Q].size() ; i++){
           il v = adj[u.Q][i] ;
           if( dist[v.P] > u.P + v.Q ){
               dist[v.P] = u.P + v.Q ;
               pq.push( li(dis[v.P] ,v.P ));
           }
      } 
  }

If you have any questions, feel free to ask in comments.

Some ways to optimize Dijkstra:

  1. Use heap. Make sure you store vertices, not edges in the heap.
  2. Try bidirectional approach. Suppose, you have to find a shortest path from S to T. You can run 2 Dijkstra's simultaneously: one from S as usual, another one from T on reversed edges. You can switch between this 2 Dijkstras using some kind of heuristic. For example, work with Dijkstra with the smallest current radius.

Theoretically, Dijkstra can be optimized to O(E + VlogV) with Fibonacci heap. But in practice it works slower.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM