求解方法:
- 求出该图的转置(所有边反向)
- 求出转置图的拓扑排序(如何求拓扑排序看出可以看我上一篇博文。文章链接)
- 根据拓扑排序的顶点顺序使用深度优先算法进行图搜索,一次搜索到的新的顶点的集合(上次遍历过的顶点不算)为一个强连通分量。
代码:
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Scanner;
/*
求解强连通分量
*/
public class StronglyConnected {
static int time=0;
static Node[] nodes; //原图
static Node[] nodes_rev; //转置图
static ArrayList<Integer> topology; // 保存转置图的拓扑顺序的倒序
public static void main(String[] args){
/**
* 输入方式:
* 第一行输入节点的个数n
* 后面n行输入第n个节点(从0开始数)链接的子节点,没有子节点则直接换行
*/
Scanner input = new Scanner(System.in);
int n = input.nextInt();
nodes = new Node[n];
nodes_rev = new Node[n];
topology = new ArrayList<>();
for(int i = 0; i < n; ++i){
nodes[i] = new Node();
}
input.nextLine();
for(int i = 0; i < n; ++i){
String line = input.nextLine();
if(!line.equals("")){
String[] tempIntStr = line.split(" ");
for(int j = 0; j < tempIntStr.length; ++j){
nodes[i].linkNodes.add(Integer.parseInt(tempIntStr[j]));
}
}
}
// 得到转置nodes_rev
revesal();
// 得到nodes_rev的拓扑排序
for(int i=0;i<nodes_rev.length;i++){
dfs_topoloy(nodes_rev[i], i);
}
// 根据得到的拓扑顺序进行dfs,每次dfs访问的新节点就是最大连通分量
HashSet<Integer> set = new HashSet<>(); // 保存已经得到的连通分量的顶点
int count = 1;
for(int i=topology.size()-1;i>=0;i--){ // 按拓扑结构的正序遍历
dfs(nodes[topology.get(i)]);
String str = "";
for(int j=0;j<nodes.length;j++){
if(nodes[j].visited == 1 && !set.contains(j)){
set.add(j);
str += j+" ";
}
}
if(str.length()!=0){
System.out.println("强连通分量"+count+": ");
System.out.print(str);
count ++;
System.out.println();
}
}
}
// 转置有向无环图
static void revesal(){
for(int i=0;i<nodes.length;i++){
for(int j : nodes[i].linkNodes){
if(nodes_rev[j] == null)
nodes_rev[j] = new Node();
nodes_rev[j].linkNodes.add(i);
}
}
}
// 求拓扑排序
static void dfs_topoloy(Node node,int index){
if(node.visited==1 || node==null)
return;
time ++;
node.d = time;
node.visited = 1;
for(int i : node.linkNodes){
Node current = nodes_rev[i];
if(current.visited == 0)
dfs_topoloy(current, i);
}
topology.add(index);
time ++;
node.f = time;
}
// 第二次dfs求连通分量
static void dfs(Node node){
if(node.visited==1 || node==null)
return;
node.visited = 1;
for(int i : node.linkNodes){
Node current = nodes[i];
if(current.visited == 0)
dfs(current);
}
}
}
结果:
以算法导论22.5节强连通分量的图(a)为例:
输入:
输出: