文章目录
- 1 API
- 2 实现和分析
- 3 测试
- 后记
1 API
检测一幅图是否还有环,如果有找出环路(任意一条),API如下:
public class | Cycle | |
---|---|---|
Cycle(Grpah G) | 预处理函数 | |
boolean | hasCycle() | |
Iterable<Interge> | cycle() | 有环给出环路,没返回null |
boolean | hasSelfLoop(Graph) | 是否有自环 |
boolean | hasParallelEdges(Graph) | 是否有平行边 |
2 实现和分析
非递归深度优先搜索实现,源代码如下;
package com.gaogzhen.datastructure.graph.undirected;
import com.gaogzhen.datastructure.stack.Stack;
import edu.princeton.cs.algs4.Graph;
import java.util.Iterator;
/**
* 检测环
* @author: Administrator
* @createTime: 2023/03/10 19:27
*/
public class Cycle {
private boolean[] marked;
private int[] edgeTo;
private Stack<Integer> cycle;
/**
* 检测是否有环,如果有给出环路
*
* @param G the undirected graph
*/
public Cycle(Graph G) {
// 如果有平行边,返回
if (hasParallelEdges(G)) {
return;
}
// don't need special case to identify self-loop as a cycle
// if (hasSelfLoop(G)) return;
marked = new boolean[G.V()];
edgeTo = new int[G.V()];
dfs(G);
}
private void dfs(Graph G) {
Stack<Node> stack = new Stack<>();
for (int v = 0; v < G.V(); v++) {
if (!marked[v]) {
if (dfs(G, -1, v, stack) ) {
return;
}
}
}
}
/**
* 深度优先搜索环路
*
* @param G 无向图
* @param u
* @param v
* @param stack
*/
private boolean dfs(Graph G, int u, int v, Stack<Node> stack) {
marked[v] = true;
Iterable<Integer> adj = G.adj(v);
if (adj != null) {
stack.push(new Node(v, u, adj.iterator()));
}
while (!stack.isEmpty()) {
Node c = stack.pop();
while (c.adj.hasNext()) {
Integer w = c.adj.next();
if (!marked[w]) {
marked[w] = true;
edgeTo[w] = c.v;
if (c.adj.hasNext()) {
stack.push(c);
}
Iterable<Integer> adjW = G.adj(w);
if (adjW != null) {
stack.push(new Node(w, c.v, adjW.iterator()));
}
break;
}
// check for cycle (but disregard reverse of edge leading to v)
else if (w != c.parent) {
cycle = new Stack<>();
for (int x = c.v; x != w; x = edgeTo[x]) {
cycle.push(x);
}
cycle.push(w);
cycle.push(c.v);
return true;
}
}
}
return false;
}
/**
* 检测无向图G是否有子环
* @param G
* @return
*/
private boolean hasSelfLoop(Graph G) {
for (int v = 0; v < G.V(); v++) {
for (int w : G.adj(v)) {
if (v == w) {
cycle = new Stack<Integer>();
cycle.push(v);
cycle.push(v);
return true;
}
}
}
return false;
}
/**
* 检测无向图G是否有平行边
* @param G
* @return
*/
private boolean hasParallelEdges(Graph G) {
marked = new boolean[G.V()];
for (int v = 0; v < G.V(); v++) {
// check for parallel edges incident to v
for (int w : G.adj(v)) {
if (marked[w]) {
cycle = new Stack<Integer>();
cycle.push(v);
cycle.push(w);
cycle.push(v);
return true;
}
marked[w] = true;
}
// reset so marked[v] = false for all v
for (int w : G.adj(v)) {
marked[w] = false;
}
}
return false;
}
/**
* 是否有环
*
* @return {@code true} if the graph has a cycle; {@code false} otherwise
*/
public boolean hasCycle() {
return cycle != null;
}
/**
* 如果有环,返回环路
* @return a cycle if the graph {@code G} has a cycle,
* and {@code null} otherwise
*/
public Iterable<Integer> cycle() {
return cycle;
}
static class Node {
/**
* 顶点索引
*/
private int v;
/**
* 顶点父结点索引
*/
private int parent;
/**
* 顶点邻接表
*/
private Iterator<Integer> adj;
public Node(int v, int parent, Iterator<Integer> adj) {
this.v = v;
this.parent = parent;
this.adj = adj;
}
}
}
检测环原理:
- 给定一个起点,开始深度优先搜索。
- 标记当前顶点v,遍历当前顶点的邻接表。获取当前邻接表顶点w,判断如果w没被标记,开始下一层搜索;
- 如果顶点w被标记过,在判断它是否等于它的爷爷顶点。如果w等于它的爷爷顶点说明是刚刚搜索过的v-w这条边;如果不是它的爷爷顶点,说明有环。
- 无向图用连接表实现,v-w和w-v是同一条边
- 没有被探索过的边v-w,但是w已经被标记过,说明在之前的探索中经过这个顶点。再次经过说明有环。
环路:edgeTo[]数组索引对应顶点,记录每个顶点到起点的路径,是一棵由父链接表示的树。如果顶点w处检测到环,那么从w的父顶点v开始记录,通过edgeTo[v]一直找到成环的顶点w,最后放入v,表示一个环。
自环检测:遍历顶点v和它的邻接表,如果邻接表中顶点定于v说明有自环。
平行边检测:一个标记数组marked[],索引对应顶点。遍历顶点v和它的邻接表,它的邻接表当前顶点w如果被标记过,说明第二次遍历,存在平行边;没有被标记过,mark[w]=true,标记。遍历完成一对顶点和邻接表之后,初始化标记数组。
3 测试
测试用无向图,从txt中读取生成图:第一行表示顶点数,第二行表示边数,第三行至末尾表示具体的边。
6
8
0 5
2 4
2 3
1 2
0 1
3 4
3 5
0 2
无向图如下所示:
测试代码:
public static void testCycle() {
String path = "H:\\gaogzhen\\java\\projects\\algorithm\\asserts\\maze.txt";
In in = new In(path);
Graph graph = new Graph(in);
Cycle cycle = new Cycle(graph);
System.out.println("是否有环:" + cycle.hasCycle());
System.out.println("环路:" + cycle.cycle());
}
搜索edgeTo[]图示:
后记
如果小伙伴什么问题或者指教,欢迎交流。
❓QQ:806797785
⭐️源代码仓库地址:https://gitee.com/gaogzhen/algorithm
参考链接:
[1][美]Robert Sedgewich,[美]Kevin Wayne著;谢路云译.算法:第4版[M].北京:人民邮电出版社,2012.10.p344-348.