检查一个图是否强连接 | Set 1(使用 DFS 的 Kosaraju)
给定一个有向图,找出该图是否是强连通的。如果任意两对顶点之间存在路径,则有向图是强连通的。例如,下面是一个强连通图。
无向图很容易,我们可以从任意顶点开始做 BFS 和 DFS。如果 BFS 或 DFS 访问所有顶点,则给定的无向图是连通的。这种方法不适用于有向图。例如,考虑以下非强连接图。如果我们从顶点 0 开始 DFS(或 BFS),我们可以到达所有顶点,但如果我们从任何其他顶点开始,我们无法到达所有顶点。
有向图怎么办?
一个简单的想法是使用像Floyd Warshall这样的全对最短路径算法或找到图的传递闭包。该方法的时间复杂度为 O(v 3 )。
我们也可以从每个顶点开始做DFS V 次。如果任何 DFS 没有访问所有顶点,则图不是强连接的。该算法需要 O(V*(V+E)) 时间,这与密集图的传递闭包相同。
一个更好的主意可以是强连通分量 (SCC)算法。我们可以在 O(V+E) 时间内找到所有 SCC。如果 SCC 的数量为 1,则图是强连接的。 SCC 的算法在找到所有 SCC 时会做额外的工作。
以下是Kosaraju 的基于 DFS 的简单算法,它对图进行两次 DFS 遍历:
1)将所有顶点初始化为未访问。
2)从任意顶点v开始对图进行DFS遍历。如果DFS遍历没有访问所有顶点,则返回false。
3)反转所有弧(或找到图的转置或反转)
4)在反向图中将所有顶点标记为未访问。
5)从相同的顶点v开始对反向图进行DFS遍历(与步骤2相同)。如果 DFS 遍历没有访问所有顶点,则返回 false。否则返回真。
这个想法是,如果每个节点都可以从顶点 v 到达,并且每个节点都可以到达 v,那么图是强连接的。在步骤 2 中,我们检查是否所有顶点都可以从 v 到达。在步骤 4 中,我们检查是否所有顶点都可以到达 v(在反转图中,如果所有顶点都可以从 v 到达,那么所有顶点都可以到达原始图中的 v)。
以下是上述算法的实现。
C++
// C++ program to check if a given directed graph is strongly
// connected or not
#include
#include
#include
using namespace std;
class Graph
{
int V; // No. of vertices
list *adj; // An array of adjacency lists
// A recursive function to print DFS starting from v
void DFSUtil(int v, bool visited[]);
public:
// Constructor and Destructor
Graph(int V) { this->V = V; adj = new list[V];}
~Graph() { delete [] adj; }
// Method to add an edge
void addEdge(int v, int w);
// The main function that returns true if the graph is strongly
// connected, otherwise false
bool isSC();
// Function that returns reverse (or transpose) of this graph
Graph getTranspose();
};
// A recursive function to print DFS starting from v
void Graph::DFSUtil(int v, bool visited[])
{
// Mark the current node as visited and print it
visited[v] = true;
// Recur for all the vertices adjacent to this vertex
list::iterator i;
for (i = adj[v].begin(); i != adj[v].end(); ++i)
if (!visited[*i])
DFSUtil(*i, visited);
}
// Function that returns reverse (or transpose) of this graph
Graph Graph::getTranspose()
{
Graph g(V);
for (int v = 0; v < V; v++)
{
// Recur for all the vertices adjacent to this vertex
list::iterator i;
for(i = adj[v].begin(); i != adj[v].end(); ++i)
{
g.adj[*i].push_back(v);
}
}
return g;
}
void Graph::addEdge(int v, int w)
{
adj[v].push_back(w); // Add w to v’s list.
}
// The main function that returns true if graph is strongly connected
bool Graph::isSC()
{
// St1p 1: Mark all the vertices as not visited (For first DFS)
bool visited[V];
for (int i = 0; i < V; i++)
visited[i] = false;
// Step 2: Do DFS traversal starting from first vertex.
DFSUtil(0, visited);
// If DFS traversal doesn’t visit all vertices, then return false.
for (int i = 0; i < V; i++)
if (visited[i] == false)
return false;
// Step 3: Create a reversed graph
Graph gr = getTranspose();
// Step 4: Mark all the vertices as not visited (For second DFS)
for(int i = 0; i < V; i++)
visited[i] = false;
// Step 5: Do DFS for reversed graph starting from first vertex.
// Starting Vertex must be same starting point of first DFS
gr.DFSUtil(0, visited);
// If all vertices are not visited in second DFS, then
// return false
for (int i = 0; i < V; i++)
if (visited[i] == false)
return false;
return true;
}
// Driver program to test above functions
int main()
{
// Create graphs given in the above diagrams
Graph g1(5);
g1.addEdge(0, 1);
g1.addEdge(1, 2);
g1.addEdge(2, 3);
g1.addEdge(3, 0);
g1.addEdge(2, 4);
g1.addEdge(4, 2);
g1.isSC()? cout << "Yes\n" : cout << "No\n";
Graph g2(4);
g2.addEdge(0, 1);
g2.addEdge(1, 2);
g2.addEdge(2, 3);
g2.isSC()? cout << "Yes\n" : cout << "No\n";
return 0;
}
Java
// Java program to check if a given directed graph is strongly
// connected or not
import java.io.*;
import java.util.*;
import java.util.LinkedList;
// This class represents a directed graph using adjacency
// list representation
class Graph
{
private int V; // No. of vertices
private LinkedList adj[]; //Adjacency List
//Constructor
Graph(int v)
{
V = v;
adj = new LinkedList[v];
for (int i=0; i i = adj[v].iterator();
while (i.hasNext())
{
n = i.next();
if (!visited[n])
DFSUtil(n,visited);
}
}
// Function that returns transpose of this graph
Graph getTranspose()
{
Graph g = new Graph(V);
for (int v = 0; v < V; v++)
{
// Recur for all the vertices adjacent to this vertex
Iterator i = adj[v].listIterator();
while (i.hasNext())
g.adj[i.next()].add(v);
}
return g;
}
// The main function that returns true if graph is strongly
// connected
Boolean isSC()
{
// Step 1: Mark all the vertices as not visited
// (For first DFS)
Boolean visited[] = new Boolean[V];
for (int i = 0; i < V; i++)
visited[i] = false;
// Step 2: Do DFS traversal starting from first vertex.
DFSUtil(0, visited);
// If DFS traversal doesn't visit all vertices, then
// return false.
for (int i = 0; i < V; i++)
if (visited[i] == false)
return false;
// Step 3: Create a reversed graph
Graph gr = getTranspose();
// Step 4: Mark all the vertices as not visited (For
// second DFS)
for (int i = 0; i < V; i++)
visited[i] = false;
// Step 5: Do DFS for reversed graph starting from
// first vertex. Starting Vertex must be same starting
// point of first DFS
gr.DFSUtil(0, visited);
// If all vertices are not visited in second DFS, then
// return false
for (int i = 0; i < V; i++)
if (visited[i] == false)
return false;
return true;
}
public static void main(String args[])
{
// Create graphs given in the above diagrams
Graph g1 = new Graph(5);
g1.addEdge(0, 1);
g1.addEdge(1, 2);
g1.addEdge(2, 3);
g1.addEdge(3, 0);
g1.addEdge(2, 4);
g1.addEdge(4, 2);
if (g1.isSC())
System.out.println("Yes");
else
System.out.println("No");
Graph g2 = new Graph(4);
g2.addEdge(0, 1);
g2.addEdge(1, 2);
g2.addEdge(2, 3);
if (g2.isSC())
System.out.println("Yes");
else
System.out.println("No");
}
}
// This code is contributed by Aakash Hasija
Python3
# Python program to check if a given directed graph is strongly
# connected or not
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.graph = defaultdict(list) # default dictionary to store graph
# function to add an edge to graph
def addEdge(self, u, v):
self.graph[u].append(v)
# A function used by isSC() to perform DFS
def DFSUtil(self, v, visited):
# Mark the current node as visited
visited[v] = True
# Recur for all the vertices adjacent to this vertex
for i in self.graph[v]:
if visited[i] == False:
self.DFSUtil(i, visited)
# Function that returns reverse (or transpose) of this graph
def getTranspose(self):
g = Graph(self.V)
# Recur for all the vertices adjacent to this vertex
for i in self.graph:
for j in self.graph[i]:
g.addEdge(j, i)
return g
# The main function that returns true if graph is strongly connected
def isSC(self):
# Step 1: Mark all the vertices as not visited (For first DFS)
visited =[False]*(self.V)
# Step 2: Do DFS traversal starting from first vertex.
self.DFSUtil(0,visited)
# If DFS traversal doesnt visit all vertices, then return false
if any(i == False for i in visited):
return False
# Step 3: Create a reversed graph
gr = self.getTranspose()
# Step 4: Mark all the vertices as not visited (For second DFS)
visited =[False]*(self.V)
# Step 5: Do DFS for reversed graph starting from first vertex.
# Starting Vertex must be same starting point of first DFS
gr.DFSUtil(0,visited)
# If all vertices are not visited in second DFS, then
# return false
if any(i == False for i in visited):
return False
return True
# Create a graph given in the above diagram
g1 = Graph(5)
g1.addEdge(0, 1)
g1.addEdge(1, 2)
g1.addEdge(2, 3)
g1.addEdge(3, 0)
g1.addEdge(2, 4)
g1.addEdge(4, 2)
print ("Yes" if g1.isSC() else "No")
g2 = Graph(4)
g2.addEdge(0, 1)
g2.addEdge(1, 2)
g2.addEdge(2, 3)
print ("Yes" if g2.isSC() else "No")
# This code is contributed by Neelam Yadav
Javascript
输出:
Yes
No