加权图中的最短路径,其中边的权重为 1 或 2
给定一个有向图,其中每条边的权重为 1 或 2,找到从给定源顶点 's' 到给定目标顶点 't' 的最短路径。预期时间复杂度为 O(V+E)。
一个简单的解决方案是使用 Dijkstra 的最短路径算法,我们可以在 O(E + VLogV) 时间内得到最短路径。
如何在 O(V+E) 时间内完成?这个想法是使用 BFS。关于 BFS 的一个重要观察是,BFS 中使用的路径在任意两个顶点之间的边数总是最少的。因此,如果所有边的权重相同,我们可以使用 BFS 找到最短路径。对于这个问题,我们可以修改图,将所有权重为 2 的边分割成两条权重为 1 的边。在修改后的图中,我们可以使用 BFS 来找到最短路径。
需要多少个新的中间顶点?我们需要为每个源顶点添加一个新的中间顶点。原因很简单,如果我们在 u 和 v 之间添加一个中间顶点 x,并且如果我们在 y 和 z 之间添加相同的顶点,那么新的路径 u 到 z 和 y 到 v 被添加到图中,这可能在原始图中已经存在.因此,在具有 V 个顶点的图中,我们需要 V 个额外的顶点。
下面是上述想法的 C++ 实现。在下面的实现中,在图中创建了 2*V 个顶点,对于每条边 (u, v),我们将其分成两条边 (u, u+V) 和 (u+V, w)。通过这种方式,我们确保为每个源顶点添加不同的中间顶点。
C/C++
// Program to shortest path from a given source vertex ‘s’ to
// a given destination vertex ‘t’. Expected time complexity
// is O(V+E).
#include
using namespace std;
// This class represents a directed graph using adjacency
// list representation
class Graph
{
int V; // No. of vertices
list *adj; // adjacency lists
public:
Graph(int V); // Constructor
void addEdge(int v, int w, int weight); // adds an edge
// finds shortest path from source vertex ‘s’ to
// destination vertex ‘d’.
int findShortestPath(int s, int d);
// print shortest path from a source vertex ‘s’ to
// destination vertex ‘d’.
int printShortestPath(int parent[], int s, int d);
};
Graph::Graph(int V)
{
this->V = V;
adj = new list[2*V];
}
void Graph::addEdge(int v, int w, int weight)
{
// split all edges of weight 2 into two
// edges of weight 1 each. The intermediate
// vertex number is maximum vertex number + 1,
// that is V.
if (weight==2)
{
adj[v].push_back(v+V);
adj[v+V].push_back(w);
}
else // Weight is 1
adj[v].push_back(w); // Add w to v’s list.
}
// To print the shortest path stored in parent[]
int Graph::printShortestPath(int parent[], int s, int d)
{
static int level = 0;
// If we reached root of shortest path tree
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;
}
// This function mainly does BFS and prints the
// shortest path from src to dest. It is assumed
// that weight of every edge is 1
int Graph::findShortestPath(int src, int dest)
{
// Mark all the vertices as not visited
bool *visited = new bool[2*V];
int *parent = new int[2*V];
// Initialize parent[] and visited[]
for (int i = 0; i < 2*V; i++)
{
visited[i] = false;
parent[i] = -1;
}
// Create a queue for BFS
list queue;
// Mark the current node as visited and enqueue it
visited[src] = true;
queue.push_back(src);
// 'i' will be used to get all adjacent vertices of a vertex
list::iterator i;
while (!queue.empty())
{
// Dequeue a vertex from queue and print it
int s = queue.front();
if (s == dest)
return printShortestPath(parent, s, dest);
queue.pop_front();
// Get all adjacent vertices of the dequeued vertex s
// If a adjacent has not been visited, then mark it
// visited and enqueue it
for (i = adj[s].begin(); i != adj[s].end(); ++i)
{
if (!visited[*i])
{
visited[*i] = true;
queue.push_back(*i);
parent[*i] = s;
}
}
}
}
// Driver program to test methods of graph class
int main()
{
// Create a graph given in the above diagram
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
// Java to shortest path from a given source vertex 's' to
// a given destination vertex 't'. Expected time complexity
// is O(V+E).
import java.util.*;
class GFG
{
// This class represents a directed graph using adjacency
// list representation
static class Graph
{
int V; // No. of vertices
Vector[] adj; // No. of vertices
static int level;
// Constructor
@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<>();
}
// adds an edge
public void addEdge(int v, int w, int weight)
{
// split all edges of weight 2 into two
// edges of weight 1 each. The intermediate
// vertex number is maximum vertex number + 1,
// that is V.
if (weight == 2)
{
adj[v].add(v + this.V);
adj[v + this.V].add(w);
} else // Weight is 1
adj[v].add(w); // Add w to v's list.
}
// print shortest path from a source vertex 's' to
// destination vertex 'd'.
public int printShortestPath(int[] parent, int s, int d)
{
level = 0;
// If we reached root of shortest path tree
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;
}
// finds shortest path from source vertex 's' to
// destination vertex 'd'.
// This function mainly does BFS and prints the
// shortest path from src to dest. It is assumed
// that weight of every edge is 1
public int findShortestPath(int src, int dest)
{
boolean[] visited = new boolean[2 * this.V];
int[] parent = new int[2 * this.V];
// Initialize parent[] and visited[]
for (int i = 0; i < 2 * this.V; i++)
{
visited[i] = false;
parent[i] = -1;
}
// Create a queue for BFS
Queue queue = new LinkedList<>();
// Mark the current node as visited and enqueue it
visited[src] = true;
queue.add(src);
while (!queue.isEmpty())
{
// Dequeue a vertex from queue and print it
int s = queue.peek();
if (s == dest)
return printShortestPath(parent, s, dest);
queue.poll();
// Get all adjacent vertices of the dequeued vertex s
// If a adjacent has not been visited, then mark it
// visited and enqueue it
for (int i : this.adj[s])
{
if (!visited[i])
{
visited[i] = true;
queue.add(i);
parent[i] = s;
}
}
}
return 0;
}
}
// Driver Code
public static void main(String[] args)
{
// Create a graph given in the above diagram
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));
}
}
// This code is contributed by
// sanjeev2552
Python
''' Program to shortest path from a given source vertex s to
a given destination vertex t. Expected time complexity
is O(V+E)'''
from collections import defaultdict
#This class represents a directed graph using adjacency list representation
class Graph:
def __init__(self,vertices):
self.V = vertices #No. of vertices
self.V_org = vertices
self.graph = defaultdict(list) # default dictionary to store graph
# function to add an edge to graph
def addEdge(self,u,v,w):
if w == 1:
self.graph[u].append(v)
else:
'''split all edges of weight 2 into two
edges of weight 1 each. The intermediate
vertex number is maximum vertex number + 1,
that is V.'''
self.graph[u].append(self.V)
self.graph[self.V].append(v)
self.V = self.V + 1
# To print the shortest path stored in parent[]
def printPath(self, parent, j):
Path_len = 1
if parent[j] == -1 and j < self.V_org : #Base Case : If j is source
print j,
return 0 # when parent[-1] then path length = 0
l = self.printPath(parent , parent[j])
#increment path length
Path_len = l + Path_len
# print node only if its less than original node length.
# i.e do not print any new node that has been added later
if j < self.V_org :
print j,
return Path_len
''' This function mainly does BFS and prints the
shortest path from src to dest. It is assumed
that weight of every edge is 1'''
def findShortestPath(self,src, dest):
# Mark all the vertices as not visited
# Initialize parent[] and visited[]
visited =[False]*(self.V)
parent =[-1]*(self.V)
# Create a queue for BFS
queue=[]
# Mark the source node as visited and enqueue it
queue.append(src)
visited[src] = True
while queue :
# Dequeue a vertex from queue
s = queue.pop(0)
# if s = dest then print the path and return
if s == dest:
return self.printPath(parent, s)
# Get all adjacent vertices of the dequeued vertex s
# If a adjacent has not been visited, then mark it
# visited and enqueue it
for i in self.graph[s]:
if visited[i] == False:
queue.append(i)
visited[i] = True
parent[i] = s
# Create a graph given in the above diagram
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)),
#This code is contributed by Neelam Yadav
C#
// C# to shortest path from a given source vertex 's' to
// a given destination vertex 't'. Expected time complexity
// is O(V+E).
using System;
using System.Collections.Generic;
class GFG
{
// This class represents a directed graph using adjacency
// list representation
class Graph
{
public int V; // No. of vertices
public List[] adj; // No. of vertices
static int level;
// Constructor
public Graph(int V)
{
this.V = V;
this.adj = new List[2 * V];
for (int i = 0; i < 2 * V; i++)
this.adj[i] = new List();
}
// adds an edge
public void addEdge(int v, int w, int weight)
{
// split all edges of weight 2 into two
// edges of weight 1 each. The intermediate
// vertex number is maximum vertex number + 1,
// that is V.
if (weight == 2)
{
adj[v].Add(v + this.V);
adj[v + this.V].Add(w);
} else // Weight is 1
adj[v].Add(w); // Add w to v's list.
}
// print shortest path from a source vertex 's' to
// destination vertex 'd'.
public int printShortestPath(int[] parent, int s, int d)
{
level = 0;
// If we reached root of shortest path tree
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;
}
// finds shortest path from source vertex 's' to
// destination vertex 'd'.
// This function mainly does BFS and prints the
// shortest path from src to dest. It is assumed
// that weight of every edge is 1
public int findShortestPath(int src, int dest)
{
bool[] visited = new bool[2 * this.V];
int[] parent = new int[2 * this.V];
// Initialize parent[] and visited[]
for (int i = 0; i < 2 * this.V; i++)
{
visited[i] = false;
parent[i] = -1;
}
// Create a queue for BFS
List queue = new List();
// Mark the current node as visited and enqueue it
visited[src] = true;
queue.Add(src);
while (queue.Count != 0)
{
// Dequeue a vertex from queue and print it
int s = queue[0];
if (s == dest)
return printShortestPath(parent, s, dest);
queue.RemoveAt(0);
// Get all adjacent vertices of the dequeued vertex s
// If a adjacent has not been visited, then mark it
// visited and enqueue it
foreach (int i in this.adj[s])
{
if (!visited[i])
{
visited[i] = true;
queue.Add(i);
parent[i] = s;
}
}
}
return 0;
}
}
// Driver Code
public static void Main(String[] args)
{
// Create a graph given in the above diagram
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));
}
}
// This code is contributed by PrinciRaj1992
输出 :
Shortest Path between 0 and 3 is 0 1 3
Shortest Distance between 0 and 3 is 3
这种方法如何 O(V+E)?在最坏的情况下,所有边的权重为 2,我们需要进行 O(E) 操作来分割所有边和 2V 个顶点,因此时间复杂度变为 O(E) + O(V+E),即 O(V+ E)。