C++ Shortest Path - Dijkstra PQ Implementation, Bellman-Ford Code As Backup, and Floyd Warshall
Introduction: Shortest Paths
We've already had a blog post on graph theory, adjacency lists/matrixes for representing a graph, and BFS/DFS for traversing a graph. So how do we find the shortest path in a graph if there are edge weights? The answer is...we use the famous Dijkstra algorithm. (Assuming there are no negative edge weights.) If there are negative edge weights, then we use Bellman Ford.
What we want is, for a starting vertex/node, a list containing the shortest path from the starting node to every other node.
What we want is, for a starting vertex/node, a list containing the shortest path from the starting node to every other node.
The above graph is what we'll be using in our algorithms. You can see that there are some nodes (vertices) labeled 0 to 6, and there are some edges that each have weights on them. We want the shortest path (lowest weight total) from one node to another. To the right is the adjacency list representation of the graph, where for adjList[i], there's a vector of pair<int, int>s at each index, where the first int represents the neighbor node, and the second int represents the edge weight between them.
Djikstra's Algorithm For Shortest Paths, using Priority Queue
Djikstra's algorithm is going to maintain a list of shortest paths from the starting node to all other nodes. Initially, the starting node has 0 distance to itself, but infinity distance to all other nodes. Then, by looking at all neighbors, of the node we're processing (starting with the start node), we'll update the shortest distances by checking if we can do better.
In this implementation, we use a priority queue, which will prioritize checking the shortest edge weights first.
In this implementation, we use a priority queue, which will prioritize checking the shortest edge weights first.
Our code will generate that adjacency list, and perform the algorithm we mentioned before.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 |
// Copyright srcmake.com 2018. // C++ Example Dijkstra Algorithm For Shortest Path (With PQ/Min-Heap) /* The Dijkstra algorithm: // Initialize the graph adjacency list. adjList[i] = pair<int, int> where first is vertex, second is edge weight. // Initialize all source->vertex as infinite. // Create a PQ. // Add source to pq, where distance is 0. // While pq isn't empty... // Get min distance vertex from pq. (Call it u.) // Visit all of u's friends. For each one (called v).... // If the distance to v is shorter by going through u... // Update the distance of v. // Insert v into the pq. */ // The example graph: https://www.srcmake.com/uploads/5/3/9/0/5390645/spgraph_orig.jpg #include <iostream> #include <vector> #include <queue> using namespace std; // An adjacency list. Each adjList[i] holds a all the friends of node i. // The first int is the vertex of the friend, the second int is the edge weight. vector< vector<pair<int, int> > > FormAdjList() { // Our adjacency list. vector< vector<pair<int, int> > > adjList; // We have 7 vertices, so initialize 7 rows. const int n = 7; for(int i = 0; i < n; i++) { // Create a vector to represent a row, and add it to the adjList. vector<pair<int, int> > row; adjList.push_back(row); } // Now let's add our actual edges into the adjacency list. // See the picture here: https://www.srcmake.com/uploads/5/3/9/0/5390645/spadjlist_orig.jpg adjList[0].push_back(make_pair(1, 2)); adjList[0].push_back(make_pair(2, 3)); adjList[1].push_back(make_pair(0, 2)); adjList[1].push_back(make_pair(5, 1)); adjList[2].push_back(make_pair(0, 3)); adjList[2].push_back(make_pair(5, 2)); adjList[3].push_back(make_pair(1, 4)); adjList[3].push_back(make_pair(4, 1)); adjList[3].push_back(make_pair(6, 2)); adjList[4].push_back(make_pair(3, 1)); adjList[4].push_back(make_pair(5, 2)); adjList[4].push_back(make_pair(6, 1)); adjList[5].push_back(make_pair(1, 1)); adjList[5].push_back(make_pair(2, 2)); adjList[5].push_back(make_pair(4, 2)); adjList[5].push_back(make_pair(6, 2)); adjList[6].push_back(make_pair(3, 2)); adjList[6].push_back(make_pair(4, 1)); adjList[6].push_back(make_pair(5, 2)); // Our graph is now represented as an adjacency list. Return it. return adjList; } // Given an Adjacency List, find all shortest paths from "start" to all other vertices. vector<int> DijkstraSP(vector< vector<pair<int, int> > > &adjList, int &start) { cout << "\nGetting the shortest path from " << start << " to all other nodes.\n"; vector<int> dist; // Initialize all source->vertex as infinite. int n = adjList.size(); for(int i = 0; i < n; i++) { dist.push_back(1000000007); // Define "infinity" as necessary by constraints. } // Create a PQ. priority_queue<pair<int, int>, vector< pair<int, int> >, greater<pair<int, int> > > pq; // Add source to pq, where distance is 0. pq.push(make_pair(start, 0)); dist[start] = 0; // While pq isn't empty... while(pq.empty() == false) { // Get min distance vertex from pq. (Call it u.) int u = pq.top().first; pq.pop(); // Visit all of u's friends. For each one (called v).... for(int i = 0; i < adjList[u].size(); i++) { int v = adjList[u][i].first; int weight = adjList[u][i].second; // If the distance to v is shorter by going through u... if(dist[v] > dist[u] + weight) { // Update the distance of v. dist[v] = dist[u] + weight; // Insert v into the pq. pq.push(make_pair(v, dist[v])); } } } return dist; } void PrintShortestPath(vector<int> &dist, int &start) { cout << "\nPrinting the shortest paths for node " << start << ".\n"; for(int i = 0; i < dist.size(); i++) { cout << "The distance from node " << start << " to node " << i << " is: " << dist[i] << endl; } } int main() { cout << "Program started.\n"; // Construct the adjacency list that represents our graph. vector< vector<pair<int, int> > > adjList = FormAdjList(); // Get a list of shortest path distances for node 0. int node = 0; vector<int> dist = DijkstraSP(adjList, node); // Print the list. PrintShortestPath(dist, node); cout << "Program ended.\n"; return 0; } |
Bellman-Ford - Good For Negative Edges
Dijkstra is a greedy algorithm and will fail if there are cycles or negative edge weights. Because of this, we need the Bellman-Ford algorithm in that case. (But it's slower than Dijkstra, so use Dijkstra depending on the problem constraints.)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 |
// Copyright srcmake.com 2018. // C++ Bellman-Ford Algorithm For Shortest Path /* Use it if there are negative edge weights in a graph. */ /* Algorithm: // Create the adjacency list that represents the graph. // Initialize a list for the distances from source to each other node as infinity. (Source to itself is 0.) // Then calculate the shortest distance using... // For numNodes-1... // For each node (u)... // For each of it's neighbors (v)... // If the distance from source to v is bigger than dist[u] + weight of (u,v)... // Update dist[v] to dist[u] + weight(u,V) // If there's a negative weight cycle in the graph, then report it, by... // For each node (u)... // For each of u's neighbors (v)... // Check if it's possible to get even better (now, that we should be at shortest) // If the distance from source to v is bigger than dist[u] + weight of (u,v)... // Report problem. */ // The example graph: https://www.srcmake.com/uploads/5/3/9/0/5390645/spgraph_orig.jpg #include <iostream> #include <vector> #include <queue> using namespace std; // An adjacency list. Each adjList[i] holds a all the friends of node i. // The first int is the vertex of the friend, the second int is the edge weight. vector< vector<pair<int, int> > > FormAdjList() { // Our adjacency list. vector< vector<pair<int, int> > > adjList; // We have 7 vertices, so initialize 7 rows. const int n = 7; for(int i = 0; i < n; i++) { // Create a vector to represent a row, and add it to the adjList. vector<pair<int, int> > row; adjList.push_back(row); } // Now let's add our actual edges into the adjacency list. // See the picture here: https://www.srcmake.com/uploads/5/3/9/0/5390645/spadjlist_orig.jpg adjList[0].push_back(make_pair(1, 2)); adjList[0].push_back(make_pair(2, 3)); adjList[1].push_back(make_pair(0, 2)); adjList[1].push_back(make_pair(5, 1)); adjList[2].push_back(make_pair(0, 3)); adjList[2].push_back(make_pair(5, 2)); adjList[3].push_back(make_pair(1, 4)); adjList[3].push_back(make_pair(4, 1)); adjList[3].push_back(make_pair(6, 2)); adjList[4].push_back(make_pair(3, 1)); adjList[4].push_back(make_pair(5, 2)); adjList[4].push_back(make_pair(6, 1)); adjList[5].push_back(make_pair(1, 1)); adjList[5].push_back(make_pair(2, 2)); adjList[5].push_back(make_pair(4, 2)); adjList[5].push_back(make_pair(6, 2)); adjList[6].push_back(make_pair(3, 2)); adjList[6].push_back(make_pair(4, 1)); adjList[6].push_back(make_pair(5, 2)); // Our graph is now represented as an adjacency list. Return it. return adjList; } // Given an Adjacency List, find all shortest paths from "start" to all other vertices. vector<int> BellmanFordSP(vector< vector<pair<int, int> > > &adjList, int &start) { cout << "\nGetting the shortest path from " << start << " to all other nodes.\n"; vector<int> dist; // Initialize all source->vertex as infinite. int n = adjList.size(); for(int i = 0; i < n; i++) { dist.push_back(1000000007); // Define "infinity" as necessary by constraints. } dist[start] = 0; // Then calculate the shortest distance using... // For numNodes-1... for(int i = 0; i < n-1; i++) { // For each node (u)... for(int u = 0; u < n; u++) { // For each of it's neighbors (v)... for(int j = 0; j < adjList[u].size(); j++) { int v = adjList[u][j].first; int weight = adjList[u][j].second; // If the distance from source to v is bigger than dist[u] + weight of (u,v)... if(dist[v] > dist[u] + weight) { // Update dist[v] to dist[u] + weight(u,V) dist[v] = dist[u] + weight; } } } } // If there's a negative weight cycle in the graph, then report it, by... // For each node (u)... // For each of u's neighbors (v)... // Check if it's possible to get even better (now, that we should be at shortest) // If the distance from source to v is bigger than dist[u] + weight of (u,v)... // Report problem. return dist; } void PrintShortestPath(vector<int> &dist, int &start) { cout << "\nPrinting the shortest paths for node " << start << ".\n"; for(int i = 0; i < dist.size(); i++) { cout << "The distance from node " << start << " to node " << i << " is: " << dist[i] << endl; } } int main() { cout << "Program started.\n"; // Construct the adjacency list that represents our graph. vector< vector<pair<int, int> > > adjList = FormAdjList(); // Get a list of shortest path distances for node 0. int node = 0; vector<int> dist = BellmanFordSP(adjList, node); // Print the list. PrintShortestPath(dist, node); cout << "Program ended.\n"; return 0; } |
The results of running this should be exactly the same as Dijkstra. (But this takes longer.)
Floyd Warshall - Efficient For All Sources To All Nodes
Dijkstra and Bellman-Ford calculate the shortest path in a graph from one source node to all other nodes. But what if we need to calculate the shortest distance from all sources as the starting node, to all other nodes? That's what the Floyd Warshall algorithm is for.
We'll need an adjacency matrix representation of our graph for this problem. This time, edge weights that don't exist are set to infinity, and the distance of a node from itself is 0.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 |
// Copyright srcmake.com 2018. // C++ Example Floyd Warshall Algorithm For Shortest Path // Official webpage for this tutorial: https://www.srcmake.com/home/cpp-shortest-path-dijkstra /* The Floyd Warshall algorithm: // Create an adjacency matrix to represent the graph. // Initialize a solution matrix that's the same as the adjacency matrix. // For each vertex w... // For each vertex as source (named u). // For each vertex as destination (named v). // If vertex w is on the shortest path from u to v... // Update value of dist[u][v] */ // The example graph: https://www.srcmake.com/uploads/5/3/9/0/5390645/spgraph_orig.jpg #include <iostream> #include <vector> #include <queue> using namespace std; ////////////////////////////////////////////////////////////////////////////////////// // Create an adjacency matrix for our graph. // Our target adjMatrix: https://www.srcmake.com/uploads/5/3/9/0/5390645/spadjlist_orig.jpg vector< vector<int> > FormAdjMatrix() { // We could use an array for the adjMatrix if we knew the size, but it's safer to use a vector. vector< vector<int> > adjMatrix; // Initialize the adjMatrix so that all vertices can visit themselves. // (Basically, make an identity matrix.) const int n = 7; for(int i = 0; i < n; i++) { // Initialize the row. vector<int> row; adjMatrix.push_back(row); // Set the row to have all INFINITE's. (Except the vertex itself.) for(int j = 0; j < n; j++) { int value = 1000000007; // Infinite is 1E9 + 7 for us. if(i == j) { value = 0; } adjMatrix[i].push_back(value); } } // Our matrix is set up, we just need to set up the edges (vertex connections). // See this image: https://www.srcmake.com/uploads/5/3/9/0/5390645/adjm_2_orig.png adjMatrix[0][1] = 2; adjMatrix[1][0] = 2; adjMatrix[0][2] = 3; adjMatrix[2][0] = 3; adjMatrix[1][3] = 4; adjMatrix[3][1] = 4; adjMatrix[1][5] = 1; adjMatrix[5][1] = 1; adjMatrix[2][5] = 2; adjMatrix[5][2] = 2; adjMatrix[3][4] = 1; adjMatrix[4][3] = 1; adjMatrix[3][6] = 2; adjMatrix[6][3] = 2; adjMatrix[4][5] = 2; adjMatrix[5][4] = 2; adjMatrix[4][6] = 1; adjMatrix[6][4] = 1; adjMatrix[5][6] = 2; adjMatrix[6][5] = 2; // Our adjacency matrix is complete. return adjMatrix; } ////////////////////////////////////////////////////////////////////////////////////// // Given an Adjacency List, find all shortest paths from "start" to all other vertices. vector< vector<int> > FloydWarshall(vector< vector<int> > &adjMatrix) { cout << "Called Floyd Warshall.\n"; cout << "\nGetting the shortest path from all nodes to all other nodes.\n"; // Initialize a solution matrix that's the same as the adjacency matrix. vector< vector<int> > dist; int n = adjMatrix.size(); for(int i = 0; i < n; i++) { vector<int> row; dist.push_back(row); for(int j = 0; j < n; j++) { dist[i].push_back(adjMatrix[i][j]); } } // For each vertex w... for(int w = 0; w < n; w++) { // For each vertex as source (named u). for(int u = 0; u < n; u++) { // For each vertex as destination (named v). for(int v = 0; v < n; v++) { // If vertex w is on the shortest path from u to v... if(dist[u][w] + dist[w][v] < dist[u][v]) { // Update value of dist[u][v] dist[u][v] = dist[u][w] + dist[w][v]; } } } } return dist; } void PrintShortestPath(vector< vector<int> > &dist) { cout << dist.size() << endl; cout << "\nPrinting the shortest paths for all nodes.\n"; for(int i = 0; i < dist.size(); i++) { for(int j = 0; j < dist.size(); j++) { cout << "The distance from node " << i << " to node " << j << " is: " << dist[i][j] << endl; } cout << endl; } } int main() { cout << "Program started.\n"; // Construct the adjacency matrix that represents our graph. vector< vector<int> > adjMatrix = FormAdjMatrix(); // Get a list of shortest path distances for all nodes. vector< vector<int> > dist = FloydWarshall(adjMatrix); // Print the list. PrintShortestPath(dist); cout << "Program ended.\n"; return 0; } |
The output for node 0 is the same as Dijkstra and Bellman-Ford.
To Get The Path Itself
In our code, we saved the distance for each node from the source. But what if we need to know the actual shortest path (meaning what the nodes are, and not just the distance)?
Basically, in our distance list, we store not only the distance from "source node" to "current node", we also store the "previous node" in an int variable.
Here's some updated code to show Dijkstra's algorithm where paths are stored.
Basically, in our distance list, we store not only the distance from "source node" to "current node", we also store the "previous node" in an int variable.
Here's some updated code to show Dijkstra's algorithm where paths are stored.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 |
// Copyright srcmake.com 2018. // C++ Example Dijkstra Algorithm For Shortest Path (With PQ/Min-Heap) // Official webpage for this tutorial: https://www.srcmake.com/home/cpp-shortest-path-dijkstra /* The Dijkstra algorithm: // Initialize the graph adjacency list. adjList[i] = pair<int, int> where first is vertex, second is edge weight. // Initialize all source->vertex as infinite. // Create a PQ. // Add source to pq, where distance is 0. // While pq isn't empty... // Get min distance vertex from pq. (Call it u.) // Visit all of u's friends. For each one (called v).... // If the distance to v is shorter by going through u... // Update the distance of v. // Insert v into the pq. */ // The example graph: https://www.srcmake.com/uploads/5/3/9/0/5390645/spgraph_orig.jpg #include <iostream> #include <vector> #include <queue> using namespace std; // An adjacency list. Each adjList[i] holds a all the friends of node i. // The first int is the vertex of the friend, the second int is the edge weight. vector< vector<pair<int, int> > > FormAdjList() { // Our adjacency list. vector< vector<pair<int, int> > > adjList; // We have 7 vertices, so initialize 7 rows. const int n = 7; for(int i = 0; i < n; i++) { // Create a vector to represent a row, and add it to the adjList. vector<pair<int, int> > row; adjList.push_back(row); } // Now let's add our actual edges into the adjacency list. // See the picture here: https://www.srcmake.com/uploads/5/3/9/0/5390645/spadjlist_orig.jpg adjList[0].push_back(make_pair(1, 2)); adjList[0].push_back(make_pair(2, 3)); adjList[1].push_back(make_pair(0, 2)); adjList[1].push_back(make_pair(5, 1)); adjList[2].push_back(make_pair(0, 3)); adjList[2].push_back(make_pair(5, 2)); adjList[3].push_back(make_pair(1, 4)); adjList[3].push_back(make_pair(4, 1)); adjList[3].push_back(make_pair(6, 2)); adjList[4].push_back(make_pair(3, 1)); adjList[4].push_back(make_pair(5, 2)); adjList[4].push_back(make_pair(6, 1)); adjList[5].push_back(make_pair(1, 1)); adjList[5].push_back(make_pair(2, 2)); adjList[5].push_back(make_pair(4, 2)); adjList[5].push_back(make_pair(6, 2)); adjList[6].push_back(make_pair(3, 2)); adjList[6].push_back(make_pair(4, 1)); adjList[6].push_back(make_pair(5, 2)); // Our graph is now represented as an adjacency list. Return it. return adjList; } // Given an Adjacency List, find all shortest paths from "start" to all other vertices. vector< pair<int, int> > DijkstraSP(vector< vector<pair<int, int> > > &adjList, int &start) { cout << "\nGetting the shortest path from " << start << " to all other nodes.\n"; vector<pair<int, int> > dist; // First int is dist, second is the previous node. // Initialize all source->vertex as infinite. int n = adjList.size(); for(int i = 0; i < n; i++) { dist.push_back(make_pair(1000000007, i)); // Define "infinity" as necessary by constraints. } // Create a PQ. priority_queue<pair<int, int>, vector< pair<int, int> >, greater<pair<int, int> > > pq; // Add source to pq, where distance is 0. pq.push(make_pair(start, 0)); dist[start] = make_pair(0, start);; // While pq isn't empty... while(pq.empty() == false) { // Get min distance vertex from pq. (Call it u.) int u = pq.top().first; pq.pop(); // Visit all of u's friends. For each one (called v).... for(int i = 0; i < adjList[u].size(); i++) { int v = adjList[u][i].first; int weight = adjList[u][i].second; // If the distance to v is shorter by going through u... if(dist[v].first > dist[u].first + weight) { // Update the distance of v. dist[v].first = dist[u].first + weight; // Update the previous node of v. dist[v].second = u; // Insert v into the pq. pq.push(make_pair(v, dist[v].first)); } } } return dist; } void PrintShortestPath(vector< pair<int, int> > &dist, int &start) { cout << "\nPrinting the shortest paths for node " << start << ".\n"; for(int i = 0; i < dist.size(); i++) { cout << "The distance from node " << start << " to node " << i << " is: " << dist[i].first << endl; int currnode = i; cout << "The path is: " << currnode; while(currnode != start) { currnode = dist[currnode].second; cout << " <- " << currnode; } cout << endl << endl; } } int main() { cout << "Program started.\n"; // Construct the adjacency list that represents our graph. vector< vector<pair<int, int> > > adjList = FormAdjList(); // Get a list of shortest path distances for node 0. int node = 0; vector< pair<int, int> > dist = DijkstraSP(adjList, node); // Print the list. PrintShortestPath(dist, node); cout << "Program ended.\n"; return 0; } |
The changes are to "vector<int> dist"" to "vector< pair<int, int> > dist" in our Dijkstra function (as well as line 116 where we store the previous node), the main function where the result is returned, and in the print function.
Conclusion
It takes some time to understand shortest path algorithms, but with some practice, it becomes second nature.
Next up in the graphing algorithm series is the Ford-Fulkerson algorithm for Maximum Flow. Check out this bloc post to see it.
Next up in the graphing algorithm series is the Ford-Fulkerson algorithm for Maximum Flow. Check out this bloc post to see it.
Like this content and want more? Feel free to look around and find another blog post that interests you. You can also contact me through one of the various social media channels.
Twitter: @srcmake
Discord: srcmake#3644
Youtube: srcmake
Twitch: www.twitch.tv/srcmake
Github: srcmake
Twitter: @srcmake
Discord: srcmake#3644
Youtube: srcmake
Twitch: www.twitch.tv/srcmake
Github: srcmake



