Shortest Path Using Dijkstras Algorithm
#include <bits/stdc++.h> // Includes common standard libraries like iostream, unordered_map, list, vector, set, limits.using namespace std; // Use the standard namespace.
// Function to find the shortest path from a source node to all other nodes// in a weighted graph with NON-NEGATIVE edge weights using Dijkstra's Algorithm.// 'edges': A vector of vectors, where each inner vector {u, v, w} represents an edge u-v with weight w.// Assumed to be undirected if edges are added bidirectionally.// 'n': Total number of nodes (vertices). Assumed to be 0-indexed (0 to n-1).// 'm': Total number of edges.// 'src': The source node from which to find shortest paths.// Returns a vector containing the shortest distances from 'src' to all nodes.vector<int> shortestPath(vector<vector<int>> &edges, int n, int m, int src) { // 1. Create Adjacency List for the graph. // 'adjList' stores: Key: node, Value: list of pairs {neighbor_node, weight_to_neighbor}. unordered_map<int, list<pair<int, int>>> adjList; for(int i = 0; i < m; i++) { int u = edges[i][0]; // Source node of an edge. int v = edges[i][1]; // Destination node of an edge. int w = edges[i][2]; // Weight of the edge.
adjList[u].push_back({v, w}); // Add edge u -> v with weight w. adjList[v].push_back({u, w}); // Add edge v -> u with weight w (for undirected graph). }
// 2. Initialize Distance Array and Priority Set. // 'distance' vector: Stores the shortest distance found so far from 'src' to each node. // Initialized to INT_MAX (infinity) for all nodes except source. vector<int> distance(n, INT_MAX);
// 'st' (set): Acts as a min-priority queue. Stores pairs of {current_shortest_distance, node_id}. // `std::set` automatically keeps elements sorted by the first element (distance). set<pair<int, int>> st;
// Initialize source node's distance. distance[src] = 0; // Distance from source to itself is 0. st.insert({0, src}); // Insert the source node with its 0 distance into the set.
// 3. Dijkstra's Main Loop: Process nodes until the priority queue is empty. while(!st.empty()) { // Fetch the pair with the smallest distance from the set (which is at the beginning). pair<int, int> curr = *(st.begin());
int nodeDistance = curr.first; // The shortest distance to 'topNode' found so far. int topNode = curr.second; // The current node being processed.
// Remove the processed node from the set. st.erase(st.begin());
// Explore neighbors of the 'topNode'. for(auto neighbor_pair : adjList[topNode]) { int neighbor = neighbor_pair.first; // The neighbor node ID. int edgeWeight = neighbor_pair.second; // The weight of the edge to the neighbor.
// Relaxation step: Check if a shorter path to 'neighbor' can be found through 'topNode'. if(nodeDistance + edgeWeight < distance[neighbor]) { // If the 'neighbor' was already in the set with a larger distance, remove that old record. // This is specific to `std::set` based priority queue. auto record = st.find({distance[neighbor], neighbor}); if(record != st.end()) { st.erase(record); }
// Update the shortest distance to 'neighbor'. distance[neighbor] = nodeDistance + edgeWeight;
// Insert the 'neighbor' with its new, updated shortest distance into the set. st.insert({distance[neighbor], neighbor}); } } }
return distance; // Return the vector of shortest distances from 'src'.}
int main() { vector<vector<int>> edges; // Vector to store input edges {u, v, w}. int n, m; // 'n' for nodes, 'm' for edges. int src; // Source node.
cout << "Enter the number of nodes : "; cin >> n;
cout << "Enter the number of edges : "; cin >> m;
cout << "Enter the edges (u v w, meaning u-v with weight w): " << endl; for(int i = 0; i < m; i++) { int u, v, w; cin >> u >> v >> w; // Read source, destination, and weight. edges.push_back({u, v, w}); // Add edge to vector. }
cout << "Enter the source : "; cin >> src;
// Call the shortestPath function (Dijkstra's). vector<int> answer_distances = shortestPath(edges, n, m, src);
cout << "Shortest distances from source " << src << ":" << endl; for(int i = 0; i < n; i++) { // Iterate through all nodes (0 to n-1). cout << "Node " << i << ": "; if(answer_distances[i] == INT_MAX) { cout << "INF"; // Indicate if unreachable. } else { cout << answer_distances[i]; // Print the distance. } cout << endl; }
return 0; // Indicate successful execution.}1. Problem Statement
- Goal: Find the shortest path (minimum total weight) from a given
sourcenode to all other reachable nodes in a weighted graph with non-negative edge weights. - Key Constraint: All edge weights must be non-negative. Dijkstra’s algorithm does not work correctly with negative edge weights. For graphs with negative weights (but no negative cycles), Bellman-Ford algorithm is used. For general graphs (possibly with negative cycles), SPFA or Bellman-Ford is used, or Floyd-Warshall for all-pairs shortest paths.
- Input: List of weighted
edges (u, v, w), totalnnodes, totalmedges,src(source node). - Output: A vector
distancewheredistance[i]is the shortest path distance fromsrcto nodei.INT_MAXindicates unreachable.
2. Core Idea
Dijkstra’s algorithm is a greedy algorithm that works in a similar way to BFS, but it prioritizes visiting nodes with the smallest current shortest distance from the source.
- It maintains a set of visited nodes (or more accurately, nodes for which the shortest path has been finalized).
- It uses a min-priority queue (implemented here using a
std::setordered by distance) to efficiently select the unvisited node with the smallest distance. - When a node
uis extracted from the priority queue, its distance (distance[u]) is considered finalized. - Then, it explores all neighbors
vofuand tries to “relax” the edges: ifdistance[u] + weight(u,v)is less thandistance[v], it means a shorter path tovhas been found throughu. In this case,distance[v]is updated, andv(with its new distance) is added/updated in the priority queue.
3. Algorithm Steps (shortestPath function)
-
Build Adjacency List:
- Create an
adjList(unordered_map<int, list<pair<int, int>>>). - For each edge
(u, v, w), addvwith weightwtoadjList[u]. - Since it’s an undirected graph (implied by adding both
u -> vandv -> u), also adduwith weightwtoadjList[v].
- Create an
-
Initialize Distance Array & Priority Queue:
distance: Avector<int>of sizen, initialized toINT_MAX(representing infinity).st(orpq): Astd::set<pair<int, int>>(orstd::priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>>). This set stores(current_distance, node_id)pairs, ordered bycurrent_distance(min-heap behavior).- Set
distance[src] = 0. - Insert
{0, src}intost.
-
Dijkstra’s Main Loop: While
stis not empty:- Extract Minimum: Get the
(nodeDistance, topNode)pair with the smallestnodeDistancefromst.pair<int, int> curr = *(st.begin());int nodeDistance = curr.first;int topNode = curr.second;
- Remove from Set:
st.erase(st.begin());(Crucial to avoid reprocessing with outdated distances). - Explore Neighbors: For each
neighbor (neigh.first)withedgeWeight (neigh.second)inadjList[topNode]:- Relaxation: If
nodeDistance + neigh.second < distance[neigh.first]:- Remove Old Record (if exists): If
neigh.firstwas already in thestwith a larger distance,st.find()andst.erase()that old record. This is unique tostd::setimplementation;std::priority_queuedoesn’t support this and might push redundant entries, which are handled by checkingdistance[node] < nodeDistancewhen extracted. - Update Distance:
distance[neigh.first] = nodeDistance + neigh.second; - Insert New Record:
st.insert({distance[neigh.first], neigh.first});(Add the neighbor with its new, smaller distance).
- Remove Old Record (if exists): If
- Relaxation: If
- Extract Minimum: Get the
-
Return: The
distancevector.
4. Time and Space Complexity
- Time Complexity:
- Building
adjList:O(M). - Dijkstra’s loop: Each vertex is extracted from the
setat most once (O(V)extractions). For each extraction, its neighbors are iterated (O(E)total edge relaxations). Eachsetoperation (insert,erase,find) takesO(log V)time (since the set can contain up toVelements). - Total:
O(E log V)if usingstd::setorstd::priority_queue.- If using a Fibonacci heap (more complex data structure), it can be
O(E + V log V).
- If using a Fibonacci heap (more complex data structure), it can be
- Building
- Space Complexity:
adjList:O(V + E).distancevector:O(V).set(orpriority_queue):O(V)in the worst case (stores all vertices).- Total:
O(V + E).
5. Why std::set as a Priority Queue?
std::setstores elements in sorted order. When pairs(distance, node)are inserted, they are naturally ordered by distance (first element of pair).*(st.begin())gives the element with the smallest distance.st.erase(st.begin())removes the smallest element.- Crucially,
std::setallows efficientfindanderaseof arbitrary elements (log V), which is useful when a node’s distance is updated. This prevents outdated entries from accumulating in the “priority queue” and being processed unnecessarily, leading to a cleaner implementation. - A standard
std::priority_queuecannot efficiently remove arbitrary elements. When usingstd::priority_queue, you’d push new (shorter) paths, and when you extract a node, you’d checkif (nodeDistance > distance[topNode]) continue;to skip outdated entries.