tarjan算法找图中的环

 

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);
		}
	}
}

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值