public class GraphEdge {
private int u; // 边的起点
private int v; // 边的终点
public GraphEdge(int u, int v) {
this.u = u;
this.v = v;
}
public int getU() {
return u;
}
public void setU(int u) {
this.u = u;
}
public int getV() {
return v;
}
public void setV(int v) {
this.v = v;
}
}
public class TarjanGraphAlgorithms {
private int count; // 搜索的次序编号(时间戳)
private int[] dfn; // dfn(u)为节点u,记录图中每个节点的DFS遍历的时间戳(即次序)
private int[] low; // low(u)为u或u的子树能够追溯到的最早的栈中节点的次序号
private boolean[] inStack; // 用于记录当前节点是否在栈中
private Deque<Integer> stack;//栈, 用于回退节点
private ArrayList<GraphEdge>[] graph;//u节点和节点的边(邻接表)
private final List<Set<Integer>> sccsList = new ArrayList<>(64); // 保存强连通分量(Sccs)
private TarjanGraphAlgorithms() {
}
/**
* @return TarjanGraphAlgorithms
* @Description: 创建算法对象
* @Title: TarjanGraphAlgorithms
*/
public static TarjanGraphAlgorithms build() {
return new TarjanGraphAlgorithms();
}
/**
* @param maxSize 最大节点值
* @return TarjanGraphAlgorithms
* @Description: 初始化数据
* @Title: TarjanGraphAlgorithms
*/
public TarjanGraphAlgorithms init(int maxSize) {
int numOfNode = maxSize + 1;
count = 0;
dfn = new int[numOfNode];
low = new int[numOfNode];
stack = new ArrayDeque<>();
inStack = new boolean[numOfNode];
graph = new ArrayList[numOfNode];
for (int i = 0; i < numOfNode; i++) {
dfn[i] = -1; // 代表顶点i未被遍历
low[i] = -1;
inStack[i] = false;
graph[i] = new ArrayList<>(16);
}
return this;
}
/**
* @param nodeU 节点U
* @param nodeV 节点V
* @return TarjanGraphAlgorithms
* @Description: 添加节点U的边G(U, V)
* @Title: TarjanGraphAlgorithms
*/
public TarjanGraphAlgorithms addGraphEdge(int nodeU, int nodeV) {
if (this.graph.length > nodeU && this.graph.length > nodeV) {
this.graph[nodeU].add(new GraphEdge(nodeU, nodeV));
}
return this;
}
/**
* @param curNode 当前节点号
* @return TarjanGraphAlgorithms
* @Description: tarjan算法
* @Title: TarjanGraphAlgorithms
*/
public TarjanGraphAlgorithms tarjan(int curNode) {
// 初始化搜索的次序编号(时间戳)
dfn[curNode] = low[curNode] = count++;
stack.push(curNode);// 入栈
inStack[curNode] = true;
ArrayList<GraphEdge> list = graph[curNode];// 遍历后继节点
for (GraphEdge edge : list) {
int succNode = edge.getV();
if (dfn[succNode] == -1) { // 如果没被访问过(-1代表没有被访问)
tarjan(succNode);// 递归调用
low[curNode] = Math.min(low[curNode], low[succNode]);// 更新所能到的上层节点
} else if (inStack[succNode]) {// 如果在栈中,并且被访问过
low[curNode] = Math.min(low[curNode], dfn[succNode]);// 到栈中最上端的节点
}
}
// 发现是整个强连通分量子树里的最小根
Set<Integer> scc = new HashSet<>();
if (dfn[curNode] == low[curNode]) {
int j = -1;
while (curNode != j) {
j = stack.pop();
inStack[j] = false;
scc.add(j);
}
if (scc.size() > 1) {
sccsList.add(scc);
}
}
return this;
}
/**
* @param start 开始下标
* @param end 结束下标
* @return java.util.List<java.util.Set < java.lang.Integer>>
* @Description: 逆转执行DFS没遍历到的节点
* @Title: TarjanGraphAlgorithms
*/
public List<Set<Integer>> reverse(int start, int end) {
for (int i = start; i <= end; i++) {
if (dfn[i] == -1) {
tarjan(i);
}
}
return this.sccsList;
}
}
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
public class TarjanGraphAlgorithmsTest {
public static void main(String[] args) {
List<GraphEdge> graphEdges = new ArrayList<>();
SecureRandom sr = new SecureRandom();
int max = 10;
int min = 1;
for (int i = min; i < max; i++) {
int u = sr.nextInt(max);
if( i != u) {
graphEdges.add(new GraphEdge(u, i));
System.out.println(u+"------------"+i);
}
}
final TarjanGraphAlgorithms tarjanGraph = TarjanGraphAlgorithms.build().init(max);
for (GraphEdge ge : graphEdges) {
tarjanGraph.addGraphEdge(ge.getU(), ge.getV());
}
final List<Set<Integer>> circuitNodes = tarjanGraph.tarjan(max).reverse(min, max);
for (Set<Integer> set : circuitNodes) {
System.out.println("----------"+set);
}
}
}