Given a directed graph where every edge has weight as either 1 or 2, find the shortest path from a given source vertex ‘s’ to a given destination vertex ‘t’. Expected time complexity is O(V+E).
A Simple Solution is to use Dijkstra’s shortest path algorithm, we can get a shortest path in O(E + VLogV) time.
How to do it in O(V+E) time?
The idea is to use BFS. One important observation about BFS is that the path used in BFS always has the least number of edges between any two vertices. So if all edges are of same weight, we can use BFS to find the shortest path. For this problem, we can modify the graph and split all edges of weight 2 into two edges of weight 1 each. We can use BFS to find the shortest path in the modified graph.
How many new intermediate vertices are needed?
We need to add a new intermediate vertex for every source vertex. The reason is simple, if we add an intermediate vertex x between u and v and if we add same vertex between y and z, then new paths u to z and y to v are added to the graph which might have not been there in the original graph. Therefore in a graph with V vertices, we need V extra vertices. Below is the C++ implementation of the above idea. In the below implementation 2*V vertices are created in a graph and for every edge (u, v), we split it into two edges (u, u+V) and (u+V, w). This way, we ensure that a different intermediate vertex is added for every source vertex.
Implementation:
C++
#include<bits/stdc++.h>
using namespace std;
class Graph
{
int V;
list< int > *adj;
public :
Graph( int V);
void addEdge( int v, int w, int weight);
int findShortestPath( int s, int d);
int printShortestPath( int parent[], int s, int d);
};
Graph::Graph( int V)
{
this ->V = V;
adj = new list< int >[2*V];
}
void Graph::addEdge( int v, int w, int weight)
{
if (weight==2)
{
adj[v].push_back(v+V);
adj[v+V].push_back(w);
}
else
adj[v].push_back(w);
}
int Graph::printShortestPath( int parent[], int s, int d)
{
static int level = 0;
if (parent[s] == -1)
{
cout << "Shortest Path between " << s << " and "
<< d << " is " << s << " " ;
return level;
}
printShortestPath(parent, parent[s], d);
level++;
if (s < V)
cout << s << " " ;
return level;
}
int Graph::findShortestPath( int src, int dest)
{
bool *visited = new bool [2*V];
int *parent = new int [2*V];
for ( int i = 0; i < 2*V; i++)
{
visited[i] = false ;
parent[i] = -1;
}
list< int > queue;
visited[src] = true ;
queue.push_back(src);
list< int >::iterator i;
while (!queue.empty())
{
int s = queue.front();
if (s == dest)
return printShortestPath(parent, s, dest);
queue.pop_front();
for (i = adj[s].begin(); i != adj[s].end(); ++i)
{
if (!visited[*i])
{
visited[*i] = true ;
queue.push_back(*i);
parent[*i] = s;
}
}
}
}
int main()
{
int V = 4;
Graph g(V);
g.addEdge(0, 1, 2);
g.addEdge(0, 2, 2);
g.addEdge(1, 2, 1);
g.addEdge(1, 3, 1);
g.addEdge(2, 0, 1);
g.addEdge(2, 3, 2);
g.addEdge(3, 3, 2);
int src = 0, dest = 3;
cout << "\nShortest Distance between " << src
<< " and " << dest << " is "
<< g.findShortestPath(src, dest);
return 0;
}
|
Java
import java.util.*;
class GFG
{
static class Graph
{
int V;
Vector<Integer>[] adj;
static int level;
@SuppressWarnings ( "unchecked" )
Graph( int V)
{
this .V = V;
this .adj = new Vector[ 2 * V];
for ( int i = 0 ; i < 2 * V; i++)
this .adj[i] = new Vector<>();
}
public void addEdge( int v, int w, int weight)
{
if (weight == 2 )
{
adj[v].add(v + this .V);
adj[v + this .V].add(w);
} else
adj[v].add(w);
}
public int printShortestPath( int [] parent, int s, int d)
{
level = 0 ;
if (parent[s] == - 1 )
{
System.out.printf( "Shortest Path between" +
"%d and %d is %s " , s, d, s);
return level;
}
printShortestPath(parent, parent[s], d);
level++;
if (s < this .V)
System.out.printf( "%d " , s);
return level;
}
public int findShortestPath( int src, int dest)
{
boolean [] visited = new boolean [ 2 * this .V];
int [] parent = new int [ 2 * this .V];
for ( int i = 0 ; i < 2 * this .V; i++)
{
visited[i] = false ;
parent[i] = - 1 ;
}
Queue<Integer> queue = new LinkedList<>();
visited[src] = true ;
queue.add(src);
while (!queue.isEmpty())
{
int s = queue.peek();
if (s == dest)
return printShortestPath(parent, s, dest);
queue.poll();
for ( int i : this .adj[s])
{
if (!visited[i])
{
visited[i] = true ;
queue.add(i);
parent[i] = s;
}
}
}
return 0 ;
}
}
public static void main(String[] args)
{
int V = 4 ;
Graph g = new Graph(V);
g.addEdge( 0 , 1 , 2 );
g.addEdge( 0 , 2 , 2 );
g.addEdge( 1 , 2 , 1 );
g.addEdge( 1 , 3 , 1 );
g.addEdge( 2 , 0 , 1 );
g.addEdge( 2 , 3 , 2 );
g.addEdge( 3 , 3 , 2 );
int src = 0 , dest = 3 ;
System.out.printf( "\nShortest Distance between" +
" %d and %d is %d\n" , src,
dest, g.findShortestPath(src, dest));
}
}
|
Python
from collections import defaultdict
class Graph:
def __init__( self ,vertices):
self .V = vertices
self .V_org = vertices
self .graph = defaultdict( list )
def addEdge( self ,u,v,w):
if w = = 1 :
self .graph[u].append(v)
else :
self .graph[u].append( self .V)
self .graph[ self .V].append(v)
self .V = self .V + 1
def printPath( self , parent, j):
Path_len = 1
if parent[j] = = - 1 and j < self .V_org :
print j,
return 0
l = self .printPath(parent , parent[j])
Path_len = l + Path_len
if j < self .V_org :
print j,
return Path_len
def findShortestPath( self ,src, dest):
visited = [ False ] * ( self .V)
parent = [ - 1 ] * ( self .V)
queue = []
queue.append(src)
visited[src] = True
while queue :
s = queue.pop( 0 )
if s = = dest:
return self .printPath(parent, s)
for i in self .graph[s]:
if visited[i] = = False :
queue.append(i)
visited[i] = True
parent[i] = s
g = Graph( 4 )
g.addEdge( 0 , 1 , 2 )
g.addEdge( 0 , 2 , 2 )
g.addEdge( 1 , 2 , 1 )
g.addEdge( 1 , 3 , 1 )
g.addEdge( 2 , 0 , 1 )
g.addEdge( 2 , 3 , 2 )
g.addEdge( 3 , 3 , 2 )
src = 0 ; dest = 3
print ( "Shortest Path between %d and %d is " % (src, dest)),
l = g.findShortestPath(src, dest)
print ( "\nShortest Distance between %d and %d is %d " % (src, dest, l)),
|
C#
using System;
using System.Collections.Generic;
class GFG
{
class Graph
{
public int V;
public List< int >[] adj;
static int level;
public Graph( int V)
{
this .V = V;
this .adj = new List< int >[2 * V];
for ( int i = 0; i < 2 * V; i++)
this .adj[i] = new List< int >();
}
public void addEdge( int v, int w, int weight)
{
if (weight == 2)
{
adj[v].Add(v + this .V);
adj[v + this .V].Add(w);
} else
adj[v].Add(w);
}
public int printShortestPath( int [] parent, int s, int d)
{
level = 0;
if (parent[s] == -1)
{
Console.Write( "Shortest Path between" +
"{0} and {1} is {2} " , s, d, s);
return level;
}
printShortestPath(parent, parent[s], d);
level++;
if (s < this .V)
Console.Write( "{0} " , s);
return level;
}
public int findShortestPath( int src, int dest)
{
bool [] visited = new bool [2 * this .V];
int [] parent = new int [2 * this .V];
for ( int i = 0; i < 2 * this .V; i++)
{
visited[i] = false ;
parent[i] = -1;
}
List< int > queue = new List< int >();
visited[src] = true ;
queue.Add(src);
while (queue.Count != 0)
{
int s = queue[0];
if (s == dest)
return printShortestPath(parent, s, dest);
queue.RemoveAt(0);
foreach ( int i in this .adj[s])
{
if (!visited[i])
{
visited[i] = true ;
queue.Add(i);
parent[i] = s;
}
}
}
return 0;
}
}
public static void Main(String[] args)
{
int V = 4;
Graph g = new Graph(V);
g.addEdge(0, 1, 2);
g.addEdge(0, 2, 2);
g.addEdge(1, 2, 1);
g.addEdge(1, 3, 1);
g.addEdge(2, 0, 1);
g.addEdge(2, 3, 2);
g.addEdge(3, 3, 2);
int src = 0, dest = 3;
Console.Write( "\nShortest Distance between" +
" {0} and {1} is {2}\n" , src,
dest, g.findShortestPath(src, dest));
}
}
|
Javascript
<script>
class Graph
{
constructor(V)
{
this .V = V;
this .adj = new Array(2 * V);
this .level = 0;
for (let i = 0; i < 2 * V; i++)
this .adj[i] = [];
}
addEdge(v, w, weight)
{
if (weight == 2)
{
this .adj[v].push(v + this .V);
this .adj[v + this .V].push(w);
} else
this .adj[v].push(w);
}
printShortestPath(parent, s, d)
{
this .level = 0;
if (parent[s] == -1)
{
document.write( "Shortest Path between " + s + " and " + d + " is " + s + " " );
return this .level;
}
this .printShortestPath(parent, parent[s], d);
this .level++;
if (s < this .V)
document.write(s + " " );
return this .level;
}
findShortestPath(src, dest)
{
let visited = new Array(2 * this .V);
let parent = new Array(2 * this .V);
for (let i = 0; i < 2 * this .V; i++)
{
visited[i] = false ;
parent[i] = -1;
}
let queue = [];
visited[src] = true ;
queue.push(src);
while (queue.length > 0)
{
let s = queue[0];
if (s == dest)
return this .printShortestPath(parent, s, dest);
queue.shift();
this .adj[s].forEach(i => {
if (!visited[i]){
visited[i] = true ;
queue.push(i);
parent[i] = s;
}
});
}
return 0;
}
}
let V = 4;
let g = new Graph(V);
g.addEdge(0, 1, 2);
g.addEdge(0, 2, 2);
g.addEdge(1, 2, 1);
g.addEdge(1, 3, 1);
g.addEdge(2, 0, 1);
g.addEdge(2, 3, 2);
g.addEdge(3, 3, 2);
let src = 0, dest = 3;
document.write( "<br />Shortest Distance between " + src + " and " + dest + " is " +
g.findShortestPath(src, dest));
</script>
|
OutputShortest Path between 0 and 3 is 0 1 3
Shortest Distance between 0 and 3 is 3
Time Complexity: O(V + E)
In this algorithm, we do a Breadth First Traversal of the given graph.
The time complexity of BFS is O(V + E) where V is the number of vertices and E is the number of edges in the graph.
Space Complexity: O(V)
The space complexity for this algorithm is O(V), where V is the number of vertices in the graph.
We need to store the visited array of size V and a parent array of size V.
How is this approach O(V+E)? In worst case, all edges are of weight 2 and we need to do O(E) operations to split all edges and 2V vertices, so the time complexity becomes O(E) + O(V+E) which is O(V+E). This article is contributed by Aditya Goel.