Problem Statement
Given a weighted undirected graph having n
vertices numbered from 1
to n
and m
edges describing there are edges, where edges[i] = [ai, bj, wi], representing an edge from vertex ai
to bj
with weight wi
.
Find the shortest path between the vertex 1
and the vertex n
and if path does not exist then return a list consisting of only -1
. If there exists a path, then return a list whose first element is the weight of the path and the remaining represents the shortest path from vertex 1
to vertex n
.
Examples

Example 1:
Input: n = 5, m = 6,
edges = [
[1, 2, 2],
[2, 5, 5],
[2, 3, 4],
[1, 4, 1],
[4, 3, 3],
[3, 5, 1]
]
Output: 5 1 4 3 5
Explanation: The source vertex is 1. Hence, the shortest distance path of node 5 from the source will be 1->4->3->5 as this is the path with a minimum sum of edge weights from source to destination.

Example 2:
Input: n = 4, m = 4,
edges = [
[1, 2, 2],
[2, 3, 4],
[1, 4, 1].
[4, 3, 3]
]
Output: 1 4
Explanation: The source vertex is 1. Hence, the shortest distance path of node 4 from the source will be 1->4 as this is the path with the minimum sum of edge weights from source to destination.

Example 3:
Input: n = 3, m = 1,
edges = [
[1, 2, 2]
]
Output: -1
Explanation: As there is no path from the source 1 to destination 3, so return -1.
Different Approaches
1️⃣ Dijkstra Algorithm
Intuition:
Since the problem requires the shortest path from source node(node 1) to node n, the first thought that must come to the mind is to use Dijkstra's Algorithm.
Modification:
Since the shortest path needs from source node(node 1) to node n is required, for every node possible, its parent node must be stored via which the node is reachable through minimum distance. Hence, in the Dijkstra's Algorithm, a slight modification can be done where whenever an edge relaxation happens for a particular node, it's parent node is stored in an array.
Approach:
- Use a priority queue to process nodes by their shortest known distance.
- Initialize two array
- One to store shortest distances, with the source node set to 0 and all others to infinity.
- Another for parent nodes, setting each node as its own parent initially.
- Add the source node to the priority queue with a distance of 0.
- Extract the node with the smallest distance from the queue and perfom edge relaxation (update the distances to its neighbors if a shorter path is found). Add the updated neighbors back to the queue and update the parent of node.
- The distance array maintains the shortest distances from the source to all nodes, while the parent array tracks the path.
- If the destination node is reachable, use the parent array to trace back from the destination to the source, forming the path. If unreachable, return [-1].
Code:
#include <bits/stdc++.h>
using namespace std;
/* Define P as a shorthand
for the pair<int, int> type */
#define P pair<int,int>
class Solution {
public:
/* Function to find the shortest
path from node 1 to node n */
vector<int> shortestPath(int n, int m,
vector<vector<int>> &edges) {
// Adjacency list to store graph
vector<pair<int, int>> adj[n + 1];
// Adding the edges to the graph
for (auto it : edges) {
adj[it[0]].push_back({it[1], it[2]});
adj[it[1]].push_back({it[0], it[2]});
}
/* Using priority queue to
implement Dijkstra Algorithm */
priority_queue<P, vector<P>, greater<P>> pq;
// Distance array
vector<int> dist(n + 1, 1e9);
// Parent array
vector<int> parent(n + 1);
/* Marking each node as
its own parent initially */
for (int i = 1; i <= n; i++)
parent[i] = i;
/* Distance of source node
(node 1) to itself is zero */
dist[1] = 0;
// Push the source node to the queue.
pq.push({0, 1});
// Until the queue is empty
while (!pq.empty())
{
/* Get the pair containing node having
minimum distance from source node */
auto it = pq.top();
pq.pop();
int node = it.second; // node
int dis = it.first; // distance
// Iterate through the neighbors
for (auto it : adj[node]) {
int adjNode = it.first; // node
int edWt = it.second; // edge weight
/* If the tentative distance to
reach adjacent node is smaller
than the known distance */
if (dis + edWt < dist[adjNode]) {
// Update the known distance
dist[adjNode] = dis + edWt;
// Push the new pair to priority queue
pq.push({dis + edWt, adjNode});
/* Update the parent of the adjNode
to the recent node(where it came from) */
parent[adjNode] = node;
}
}
}
/* If distance to the node could not be found,
return an array containing -1. */
if (dist[n] == 1e9)
return {-1};
// Array to store the path
vector<int> path;
// Start from the destination node
int node = n;
/* Iterate backwards from destination
to source through the parent array */
while (parent[node] != node) {
// Add the node to the path
path.push_back(node);
// Take a step back
node = parent[node];
}
// Add the source node to the path
path.push_back(1);
/* Since the path stored is in a
reverse order, reverse the array
to get the actual path */
reverse(path.begin(), path.end());
// Add the path weight in the beginning
path.insert(path.begin(), dist[n]);
// Return the result
return path;
}
};
int main() {
int n = 5, m = 6;
vector<vector<int>> edges = {
{1,2,2}, {2,5,5}, {2,3,4},
{1,4,1}, {4,3,3}, {3,5,1}
};
/* Creating an instance of
Solution class */
Solution sol;
/* Function call to find the shortest distance
of each node from the source node */
vector<int> ans = sol.shortestPath(n, m, edges);
// Output
cout << "The resulting path weight is: " << ans[0] << endl;
cout << "The path is: " << endl;
for(int i=1; i < ans.size(); i++) {
cout << ans[i] << " ";
}
return 0;
}
Complexity Analysis:
- Time Complexity: O((N+M)*logN)
- Each node is processed once in the priority queue and deletion and insertion operation takes O(logN) time making it overall O(N*logN) in the worst case.
- For each vertex, all its edges are relaxed. This operation involves updating the priority queue, which takes O(logV) making it overall O(M*logN) for E edges in the worst case.
- Reconstructing the path involves tracing the parent array, which takes O(N) in the worst case (since we may trace back through all vertices).
- Space Complexity: O(N)
- The priority queue will store distances to all nodes in worst case leading to O(N) space.
- The distance array and parent array takes O(N) space each.
- The path array will store O(N) nodes in the worst case.