一、问题描述
如何检测无向图中的环?注意:本题中不考虑自环和平行环
二、解决思路
由于深度优先搜索性质,我们可以用它来检测环。假设当前访问顶点为v,访问v之前访问的顶点为prev,则prev为v的前驱节点。设w为v的后继节点,且w != prev并且w已被访问了,则可以判断图中存在环;假设第一个访问的顶点的前驱节点为其本身,我们以图1 为例进行讲解:
第一步:我们先访问0号顶点,此时v = 0 , prev = 0,w = 1。满足w != prev,但不满足w以访问,继续执行下一步;如下图:
第二步:我们访问1号顶点,此时v = 1,prev = 0,w = 2或3;不满足条件继续执行如下图所示:
第三步:我们访问2号顶点,此时v = 2,prev = 1,w = 1或3;不满足条件继续执行,如下图所示:
第四步:我们访问3号顶点,此时v = 3,prev = 2;两种情况,第一种情况:w = 2,v = 3,prev = 2不满足w != prev;第二种情况:w = 1,v = 3,prev = 2满足w != prev并且满足w已经访问,所以该图是一个有环图。
三、算法实现
本算法依赖上一篇文章所建立图,其图的创建代码参考上一篇文章无向图的创建(java实现),代码实现如下所示:
package graph;
import java.util.Arrays;
/**
* 通过深度优先搜索检测图中是否存在环
*
* @date 2021-01-22 15:18
* @author hh
*/
public class CycleTest {
/**
* 图对象
*/
private Graph graph;
/**
* 标记哪些顶点被访问
*/
private boolean[] marked;
/**
* 是否存在环
*/
private boolean isHasCycle;
private CycleTest() {
}
public boolean hasCycle(){
return isHasCycle;
}
public CycleTest(Graph graph) {
this.graph = graph;
//默认不存在环
this.isHasCycle = false;
marked = new boolean[graph.getVertices()];
Arrays.fill(marked,Boolean.FALSE);
this.dfs();
}
/**
* 深度优先搜索
*/
public void dfs(){
for(int v = 0; v < graph.getVertices(); v++ ){
if(!isMarked(v)){
this.search(v,v);
}
}
}
private void search(int v,int prev){
System.out.print(v +" ");
marked[v] = true;
for(Integer w : graph.adj(v)){
if(!isMarked(w)){
search(w,v);
}else if (w != prev){
isHasCycle = true;
}
}
}
/**
* 顶点w是否被访问过
* @param w :顶点编号
* @return ;是否被访问
*/
private boolean isMarked(int w){
return this.marked[w];
}
}
测试代码如下所示:
public static void main(String[] args) throws Exception {
String filename = "D:\\Desktop\\graph5.txt";
Graph graph = new Graph();
graph.createGraphByFile(filename);
CycleTest cycleTest = new CycleTest(graph);
boolean hasCycle = cycleTest.hasCycle();
System.out.print("\n是否存在环:" + hasCycle);
}