Given a weighted, undirected, and connected graph of V vertices and an adjacency list adj where adj[i] is a list of lists containing two integers where the first integer of each list j denotes there is an edge between i and j, second integers corresponds to the weight of that edge. You are given the source vertex S and You have to Find the shortest distance of all the vertex from the source vertex S. You have to return a list of integers denoting the shortest distance between each node and Source vertex S.
Note: The Graph doesn’t contain any negative weight cycle
Example 1:
Input:
V = 2
adj [] = {{{1, 9}}, {{0, 9}}}
S = 0
Output:
0 9
Explanation: :
The source vertex is 0. Hence, the shortest distance of node 0
from the source is 0 and the shortest distance of node 1 from
source will be 9.
Example 2:
Input:
V = 3, E = 3
adj = {{{1, 1}, {2, 6}}, {{2, 3}, {0, 1}}, {{1, 3}, {0, 6}}}
S = 2
Output:
4 3 0
Explanation:
For nodes 2 to 0, we can follow the path 2-1-0.
This has a distance of 1+3 = 4, whereas the path 2-0
has a distance of 6. So, the Shortest path from 2 to 0 is 4.
The shortest distance from 0 to 1 is 1.
Solution
Disclaimer: Don’t jump directly to the solution, try it out yourself first.
Note: In case any image/dry run is not clear please refer to the video attached at the bottom.
Approach:
We’ll be using Set in this approach for finding the shortest distances from the source node to every other node through Dijkstra’s Algorithm.
Initial configuration:
Source Node: Before starting off with the Algorithm, we need to define a source node from which the shortest distances to every other node would be calculated.
Set: Define a Set that would contain pairs of the type {dist, node}, where ‘dist’ indicates the currently updated value of the shortest distance from the source to the ‘node’.
Dist Array: Define a dist array initialized with a large integer number at the start indicating that the nodes are unvisited initially. This array stores the shortest distances to all the nodes from the source node.
The Algorithm consists of the following steps :
We would be using a set and a distance array of size V (where ‘V’ are the number of nodes in the graph) initialized with infinity (indicating that at present none of the nodes are reachable from the source node) and initialize the distance to source node as 0.
We push the source node to the set along with its distance which is also 0.
Now, we start erasing the elements from the set and look out for their adjacent nodes one by one. If the current reachable distance is better than the previous distance indicated by the distance array, we update the distance and insert it in the set.
A node with a lower distance would be first erased from the set as opposed to a node with a higher distance. By following step 3, until our set becomes empty, we would get the minimum distance from the source node to all other nodes. We can then return the distance array.
The only difference between using a Priority Queue and a Set is that in a set we can check if there exists a pair with the same node but a greater distance than the current inserted node as there will be no point in keeping that node into the set if we come across a much better value than that. So, we simply delete the element with a greater distance value for the same node.
Here’s a quick demonstration of the algorithm :
Note: Dijkstra’s Algorithm is not valid for negative weights or negative cycles.
We can understand that by looking at the illustration below :
Here, we initially mark the source node ‘0’ as 0 and node ‘1’ as INFINITY (as it is unvisited). Now, when we start applying the above algorithm on this we notice that both the nodes are updated each time we come to them again. This is due to the negative weight of ‘-2’ which makes the total distance to the node always lesser than the previous value. Therefore, due to inaccurate results, we assume the graph to always contain positive weights while using Dijkstra’s Algorithm.
Note: If you wish to see the dry run of the above approach, you can watch the video attached to this article.
Intuition:
The above problem implements a BFS kind of approach using the set data structure. The only thing that we need to take care of is that for all the paths possible between a pair of nodes, we need to store the path with the minimum cost between them. That is, say we have a node that has been reached by two paths, one with a cost of 7 and another with a cost of say 9. It is obvious that the path with a cost of 7 would be more optimal than the path with a cost of 9. A set data structure in C++ always stores the elements in increasing order i.e., when we erase from a set, the smallest valued elements get erased first.
Code:
C++ Code
#include<bits/stdc++.h>
using namespace std;
class Solution
{
public:
//Function to find the shortest distance of all the vertices
//from the source vertex S.
vector <int> dijkstra(int V, vector<vector<int>> adj[], int S)
{
// Create a set ds for storing the nodes as a pair {dist,node}
// where dist is the distance from source to the node.
// set stores the nodes in ascending order of the distances
set<pair<int,int>> st;
// Initialising dist list with a large number to
// indicate the nodes are unvisited initially.
// This list contains distance from source to the nodes.
vector<int> dist(V, 1e9);
st.insert({0, S});
// Source initialised with dist=0
dist[S] = 0;
// Now, erase the minimum distance node first from the set
// and traverse for all its adjacent nodes.
while(!st.empty()) {
auto it = *(st.begin());
int node = it.second;
int dis = it.first;
st.erase(it);
// Check for all adjacent nodes of the erased
// element whether the prev dist is larger than current or not.
for(auto it : adj[node]) {
int adjNode = it[0];
int edgW = it[1];
if(dis + edgW < dist[adjNode]) {
// erase if it was visited previously at
// a greater cost.
if(dist[adjNode] != 1e9)
st.erase({dist[adjNode], adjNode});
// If current distance is smaller,
// push it into the queue
dist[adjNode] = dis + edgW;
st.insert({dist[adjNode], adjNode});
}
}
}
// Return the list containing shortest distances
// from source to all the nodes.
return dist;
}
};
int main()
{
// Driver code.
int V = 3, E = 3, S = 2;
vector<vector<int>> adj[V];
vector<vector<int>> edges;
vector<int> v1{1, 1}, v2{2, 6}, v3{2, 3}, v4{0, 1}, v5{1, 3}, v6{0, 6};
int i = 0;
adj[0].push_back(v1);
adj[0].push_back(v2);
adj[1].push_back(v3);
adj[1].push_back(v4);
adj[2].push_back(v5);
adj[2].push_back(v6);
Solution obj;
vector<int> res = obj.dijkstra(V, adj, S);
for (int i = 0; i < V; i++)
{
cout << res[i] << " ";
}
cout << endl;
return 0;
}
Output:
4 3 0
Note: This set approach cannot be implemented in JAVA by using TreeSet or HashSet. For implementing Dijkstra’s Algorithm in JAVA, we would use a priority queue only.
Time Complexity : O( E log(V) )
Where E = Number of edges and V = Number of Nodes.
Space Complexity : O( |E| + |V| )
Where E = Number of edges and V = Number of Nodes.
Special thanks to Priyanshi Goel for contributing to this article on takeUforward. If you also wish to share your knowledge with the takeUforward fam, please check out this article. If you want to suggest any improvement/correction in this article please mail us at [email protected]