原文: https://www.programiz.com/dsa/strongly-connected-components
在本教程中,您将学习如何形成强连通的组件。 此外,您还将在 C,C++ ,Java 和 Python 中找到 kosararju 算法的工作示例。
强连通的组件是有向图的一部分,其中从每个顶点到另一个顶点都有一条路径。 仅适用于有向图。
例如:
让我们来看看下图。

初始的图
上图的强连通的组件是:

强连通组件
您可以观察到,在第一个强连接的组件中,每个顶点都可以通过定向路径到达另一个顶点。
可以使用 Kosaraju 算法找到这些组件。
Kosaraju 算法
Kosaraju 算法基于两次实现的深度优先搜索算法。
涉及三个步骤。
在整个图上执行深度优先搜索。
让我们从顶点 0 开始,访问其所有子顶点,并将访问的顶点标记为完成。 如果顶点通向已经访问过的顶点,则将该顶点推入栈。
例如:从顶点 0 开始,转到顶点 1,顶点 2,然后到达顶点 3。 顶点 3 导致已经访问过的顶点 0,因此将源顶点(即顶点 3)压入栈。
图上的 DFS
转到上一个顶点(顶点 2),并访问其子顶点,即顶点 4,顶点 5,顶点 6 和顶点 7 顺序。 由于顶点 7 无处可去,因此将其推入栈。
图上的 DFS 转到上一个顶点(顶点 6),并访问其子顶点。 但是,它的所有子顶点都已访问,因此将其推入栈。
堆叠
类似地,创建最终堆叠。
最终筹码反转原始图。
反转图上的 DFS对反向图执行深度优先搜索。
从栈的顶部顶点开始。 遍历其所有子顶点。 一旦到达已经访问过的顶点,就会形成一个强连通的组件。
例如:从栈中弹出顶点 0。 从顶点 0 开始,遍历其子顶点(依次为顶点 0,顶点 1,顶点 2,顶点 3)并将它们标记为已访问。 顶点 3 的子级已经被访问过,因此这些访问过的顶点形成一个强连接的组件。
从顶部开始,并遍历所有顶点
转到栈并弹出顶部顶点(如果已访问)。 否则,请从栈中选择顶部顶点,然后遍历其子顶点,如上所示。
如果已经访问过,则弹出顶部顶点
强连通的组件因此,强连接的组件为:
所有强连接的组件
Python,Java,C++ 示例
# Kosaraju's algorithm to find strongly connected components in Pythonfrom collections import defaultdictclass Graph:def __init__(self, vertex):self.V = vertexself.graph = defaultdict(list)# Add edge into the graphdef add_edge(self, s, d):self.graph[s].append(d)# dfsdef dfs(self, d, visited_vertex):visited_vertex[d] = Trueprint(d, end='')for i in self.graph[d]:if not visited_vertex[i]:self.dfs(i, visited_vertex)def fill_order(self, d, visited_vertex, stack):visited_vertex[d] = Truefor i in self.graph[d]:if not visited_vertex[i]:self.fill_order(i, visited_vertex, stack)stack = stack.append(d)# transpose the matrixdef transpose(self):g = Graph(self.V)for i in self.graph:for j in self.graph[i]:g.add_edge(j, i)return g# Print stongly connected componentsdef print_scc(self):stack = []visited_vertex = [False] * (self.V)for i in range(self.V):if not visited_vertex[i]:self.fill_order(i, visited_vertex, stack)gr = self.transpose()visited_vertex = [False] * (self.V)while stack:i = stack.pop()if not visited_vertex[i]:gr.dfs(i, visited_vertex)print("")g = Graph(8)g.add_edge(0, 1)g.add_edge(1, 2)g.add_edge(2, 3)g.add_edge(2, 4)g.add_edge(3, 0)g.add_edge(4, 5)g.add_edge(5, 6)g.add_edge(6, 4)g.add_edge(6, 7)print("Strongly Connected Components:")g.print_scc()
// Kosaraju's algorithm to find strongly connected components in Javaimport java.util.*;import java.util.LinkedList;class Graph {private int V;private LinkedList<Integer> adj[];// Create a graphGraph(int s) {V = s;adj = new LinkedList[s];for (int i = 0; i < s; ++i)adj[i] = new LinkedList();}// Add edgevoid addEdge(int s, int d) {adj[s].add(d);}// DFSvoid DFSUtil(int s, boolean visitedVertices[]) {visitedVertices[s] = true;System.out.print(s + " ");int n;Iterator<Integer> i = adj[s].iterator();while (i.hasNext()) {n = i.next();if (!visitedVertices[n])DFSUtil(n, visitedVertices);}}// Transpose the graphGraph Transpose() {Graph g = new Graph(V);for (int s = 0; s < V; s++) {Iterator<Integer> i = adj[s].listIterator();while (i.hasNext())g.adj[i.next()].add(s);}return g;}void fillOrder(int s, boolean visitedVertices[], Stack stack) {visitedVertices[s] = true;Iterator<Integer> i = adj[s].iterator();while (i.hasNext()) {int n = i.next();if (!visitedVertices[n])fillOrder(n, visitedVertices, stack);}stack.push(new Integer(s));}// Print strongly connected componentvoid printSCC() {Stack stack = new Stack();boolean visitedVertices[] = new boolean[V];for (int i = 0; i < V; i++)visitedVertices[i] = false;for (int i = 0; i < V; i++)if (visitedVertices[i] == false)fillOrder(i, visitedVertices, stack);Graph gr = Transpose();for (int i = 0; i < V; i++)visitedVertices[i] = false;while (stack.empty() == false) {int s = (int) stack.pop();if (visitedVertices[s] == false) {gr.DFSUtil(s, visitedVertices);System.out.println();}}}public static void main(String args[]) {Graph g = new Graph(8);g.addEdge(0, 1);g.addEdge(1, 2);g.addEdge(2, 3);g.addEdge(2, 4);g.addEdge(3, 0);g.addEdge(4, 5);g.addEdge(5, 6);g.addEdge(6, 4);g.addEdge(6, 7);System.out.println("Strongly Connected Components:");g.printSCC();}}
// Kosaraju's algorithm to find strongly connected components in C++#include <iostream>#include <list>#include <stack>using namespace std;class Graph {int V;list<int> *adj;void fillOrder(int s, bool visitedV[], stack<int> &Stack);void DFS(int s, bool visitedV[]);public:Graph(int V);void addEdge(int s, int d);void printSCC();Graph transpose();};Graph::Graph(int V) {this->V = V;adj = new list<int>[V];}// DFSvoid Graph::DFS(int s, bool visitedV[]) {visitedV[s] = true;cout << s << " ";list<int>::iterator i;for (i = adj[s].begin(); i != adj[s].end(); ++i)if (!visitedV[*i])DFS(*i, visitedV);}// TransposeGraph Graph::transpose() {Graph g(V);for (int s = 0; s < V; s++) {list<int>::iterator i;for (i = adj[s].begin(); i != adj[s].end(); ++i) {g.adj[*i].push_back(s);}}return g;}// Add edge into the graphvoid Graph::addEdge(int s, int d) {adj[s].push_back(d);}void Graph::fillOrder(int s, bool visitedV[], stack<int> &Stack) {visitedV[s] = true;list<int>::iterator i;for (i = adj[s].begin(); i != adj[s].end(); ++i)if (!visitedV[*i])fillOrder(*i, visitedV, Stack);Stack.push(s);}// Print strongly connected componentvoid Graph::printSCC() {stack<int> Stack;bool *visitedV = new bool[V];for (int i = 0; i < V; i++)visitedV[i] = false;for (int i = 0; i < V; i++)if (visitedV[i] == false)fillOrder(i, visitedV, Stack);Graph gr = transpose();for (int i = 0; i < V; i++)visitedV[i] = false;while (Stack.empty() == false) {int s = Stack.top();Stack.pop();if (visitedV[s] == false) {gr.DFS(s, visitedV);cout << endl;}}}int main() {Graph g(8);g.addEdge(0, 1);g.addEdge(1, 2);g.addEdge(2, 3);g.addEdge(2, 4);g.addEdge(3, 0);g.addEdge(4, 5);g.addEdge(5, 6);g.addEdge(6, 4);g.addEdge(6, 7);cout << "Strongly Connected Components:\n";g.printSCC();}
Kosaraju 算法复杂度
Kosaraju 算法在线性时间即O(V+E)中运行。
强连通的组件应用
- 车辆路线选择应用
- 地图
- 形式验证中的模型检查
