该算法分为三步: 1. DFS算法计算完成时间。DFS算法可以计算出每个节点的发现时间与完成时间。 2. 计算逆图,所谓逆图,就是将边进行反向后产生的图。 3. 在逆图上继续DFS,因为前面正向了,如果反向还能访问,那无疑是一个强连接组件。 DFS以最大访问时间的节点开始,如此DFS找出所有的强连接组件。
package com.lpl.graph.korasaju;
import java.util.*;
/**
* @author lpl
* @date 2022-10-17 14:31
*/
/* 该算法分为三步:
1. DFS算法计算完成时间。DFS算法可以计算出每个节点的发现时间与完成时间。
2. 计算逆图,所谓逆图,就是将边进行反向后产生的图。
3. 在逆图上继续DFS,因为前面正向了,如果反向还能访问,那无疑是一个强连接组件。
DFS以最大访问时间的节点开始,如此DFS找出所有的强连接组件。
*/
public class KorasajuTest {
public static void main(String[] args) {
HashMap<String, LinkedList<String>> map = new HashMap<>();
map.put("A", setAdjacencyList("F"));
map.put("B", setAdjacencyList("A", "C", "G"));
map.put("C", setAdjacencyList("D", "G"));
map.put("D", setAdjacencyList("I"));
map.put("E", setAdjacencyList("D", "J"));
map.put("F", setAdjacencyList("B"));
map.put("G", setAdjacencyList("H", "K", "L"));
map.put("H", setAdjacencyList("D", "I", "L"));
map.put("I", setAdjacencyList("C", "M"));
map.put("J", setAdjacencyList("E", "I"));
map.put("K", setAdjacencyList("L"));
map.put("L", setAdjacencyList("M"));
map.put("M", setAdjacencyList("L"));
// System.out.println(map.size());
Graph graph = new Graph();
graph.setMap(map);
System.out.println("原图的邻接链表形式:");
print(graph.getMap());
System.out.print("dfs结果: ");
graph.dfs();
System.out.println("\n各顶点的搜索时间和完成时间: ");
graph.showDiscoveredTimeAndCompletedTime();
HashMap<String, LinkedList<String>> inverseGraph = getInverseGraph(map);
graph.setMap(inverseGraph);
System.out.println("\n逆图的邻接链表形式:");
print(graph.getMap());
//按完成时间从大到小的顺序, 完成逆图的dfs
ArrayList<String> sortVertex = graph.sortVertex();
graph.dfs(sortVertex);
}
//获得逆图
public static HashMap<String, LinkedList<String>> getInverseGraph(HashMap<String, LinkedList<String>> map) {
HashMap<String, LinkedList<String>> reversedMap = new HashMap();
Set<String> keySet = map.keySet();
for (String ver : keySet) {
LinkedList<String> list = map.get(ver); //ver 点的邻接链表
for (String x : list) {
if (reversedMap.containsKey(x)) {
reversedMap.get(x).addLast(ver);
reversedMap.put(x, reversedMap.get(x));
} else {
reversedMap.put(x, setAdjacencyList(ver));
}
}
}
return reversedMap;
}
//设置某个顶点的邻接链表
public static LinkedList<String> setAdjacencyList(String... vertexes) {
LinkedList<String> list = new LinkedList<>();
for (int i = 0; i < vertexes.length; i++) {
list.add(vertexes[i]);
}
return list;
}
//打印
public static void print(HashMap map) {
for (Object obj : map.keySet()) {
String ver = (String)obj;
System.out.println(ver + ": " + map.get(ver));
}
}
}
class Graph {
private HashMap<String, LinkedList<String>> map;
private HashSet<String> isVisited; //记录是否访问
private HashMap<String, Integer> discoveredTime; //顶点被搜索的时间
private HashMap<String, Integer> completedTime; //顶点已完成搜索的时间
private int count; //用于辅助记录上面两个时间
public void setMap(HashMap<String, LinkedList<String>> map) {
this.map = map;
}
public Graph() {
this.isVisited = new HashSet<>();
this.discoveredTime = new HashMap<>();
this.completedTime = new HashMap<>();
this.count = 0;
}
//处理非全连通情况, 按完成时间从大到小的顺序, 完成逆图的dfs
public void dfs(ArrayList<String> list) {
isVisited.clear();
for (String ver: list) {
if (!isVisited.contains(ver)) {
System.out.print("\n**强连通分量 ");
// dfsBysatck(ver);
dfsByRecursion(ver);
System.out.print(" 连通分量**");
}
}
}
//按完成时间从大到小的顺序, 完成逆图的dfs
public ArrayList<String> sortVertex() {
//存储各顶点的存储时间, 后面降序整理
ArrayList<Integer> vals = new ArrayList<>();
//集合 completedTime 的 key
Set<String> keySet = completedTime.keySet();
//填充 vals
for (String ver: keySet) {
vals.add(completedTime.get(ver));
}
//降序排列 vals
vals.sort(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
});
/* 转换键值对, 用 tmpMap 来接收*/
HashMap<Integer, String> tmpMap = new HashMap<>();
for (String ver: keySet) {
tmpMap.put(completedTime.get(ver), ver);
}
//res 用于返回
ArrayList<String> res = new ArrayList<>();
//根据排序好的 vals 来填充 res
for (Integer val: vals) {
res.add(tmpMap.get(val));
}
return res;
}
//处理非全连通情况
public void dfs() {
isVisited.clear();
for (String ver: map.keySet()) {
if (!isVisited.contains(ver)) {
System.out.print("\n**连通开始 ");
// dfsBysatck(ver);
dfsByRecursion(ver);
System.out.print(" 连通结束**");
}
}
}
//dfs: 递归
public void dfsByRecursion(String ver) {
System.out.print(ver + "->");
//记录搜索时间
discoveredTime.put(ver, ++count );
//标记
isVisited.add(ver);
//拿到 ver 的邻接结点集合
LinkedList<String> list = map.get(ver);
for (String w : list) {
if (!isVisited.contains(w)) {
dfsByRecursion(w);
}
}
//记录完成时间
completedTime.put(ver, ++count);
}
public HashMap<String, LinkedList<String>> getMap() {
return map;
}
public HashSet<String> getIsVisited() {
return isVisited;
}
public void showDiscoveredTimeAndCompletedTime() {
for (String ver: discoveredTime.keySet()) {
System.out.println(ver + ": (" + discoveredTime.get(ver) + ", " + completedTime.get(ver) + ")");
}
}
}
测试用例:
运行结果: