作业要求:按照教材中对有向无环图(DAG)的定义与判断方式,对图(tinyDG.txt),编程实现并判断该G是否有环,进一步找出环的路径(如:v1-v5-v6-v1)。类名:GraphDAG。
用java实现无向图的DFS算法,代码如下:
/**
* 有向无环图的判断
*/
public class GraphDAG {
//vertex,顶点数目
private int v;
//edge,边数目
private int e;
//邻接矩阵
private int [][] matrix ;
/**
* 初始化
* @param v 顶点数目
* @param e 边数目
*/
public GraphDAG(int vertex, int edge){
if(edge<0) throw new RuntimeException("Number of edges must be nonnegative");
if (edge > vertex*vertex) throw new RuntimeException("Too many edges");
this.v = vertex;
this.e = edge;
this.matrix = new int[v][v];
}
//两个顶点间存在边,设置矩阵值
public void addEdge(int v1, int v2){
matrix[v1][v2] = 1;
}
//获取邻接矩阵
public int[][] getAdjacencyMatrix(){ return matrix; }
/**
* 获取环路路径
* @param cc 强连通分量集合
* @return 各个强连通分量的环路路径
*/
public List<String> getCyclePath(List<GraphNode[]> cc){
List<String> list = new ArrayList<String>();
int count = cc.size();
//遍历每个强连通分量,在其内部寻找环路
for(int i=0; i<count; i++){
GraphNode[] c = cc.get(i);
int len = c.length;
//取出强连通分量集合的所有顶点名-->数组,方便查找
int [] sortSequence = new int[len];
int num = 0 ;
//
//sortSequence:
//name :0 1 5 4 2 3
//index:0 1 2 3 4 5
//
for(GraphNode node : c){
sortSequence[num++] = node.getName();
}
for(int j=0; j<len; j++){
//取出边(u,v)的u顶点
int vertex1 = sortSequence[j];
for(int k=0; k<len; k++){
//取出边(u,v)的v顶点
int vertex2 = sortSequence[k];
//关键!!! 在有向无环图中,每条边都指向一个post值更小的顶点( post(u)>post(v) )
//即对于回边有: post(u)<post(v)~~~
if(matrix[vertex1][vertex2] == 1 && c[j].getPost()<c[k].getPost()){
StringBuffer sb = new StringBuffer();
for(int n=k; n<j; n++){
//注意!!! 环的路径中,每条边都指向一个post值更小的顶点
if(c[n].getPost()>c[n+1].getPost()){
sb.append(sortSequence[n]+"-");
}
}
sb.append(vertex1 + "-" + vertex2);
list.add(sb.toString());
}
}
}
}
return list;
}
}
测试代码:
public static void main(String[] args){
try(Scanner scanner = new Scanner
(GraphDAG.class.getClassLoader().getResourceAsStream("tinyDG.txt"));
PrintWriter out = new PrintWriter(new File("tinyG_matrix.txt"));){
//第一行的数字是顶点的数目
int v = scanner.nextInt();
//第二行的数字是边的数目
int e = scanner.nextInt();
GraphDAG graph = new GraphDAG(v, e);
//读取每条边对应的两个顶点,设置邻接矩阵的值
for (int i = 0; i < e; i++) {
int v1 = scanner.nextInt();
int v2 = scanner.nextInt();
graph.addEdge(v1, v2);
}
//根据graph的邻接矩阵,对其进行深度优先搜索
int matrix[][] = graph.getAdjacencyMatrix();
GraphDFS graphDFS = new GraphDFS(matrix);
//使用无向图的深度优先搜索,对该有向图进行(参照"第四周作业——无向图的DFS算法")
graphDFS.dfs();
//强连通分量集合
List<GraphNode[]> cc = graphDFS.getCC();
List<String> pathList = graph.getCyclePath(cc);
if(pathList.size()>0){
System.err.println("存在环路,环路路径如下:\n");
int num = 1;
for(String path : pathList){
System.err.println("第" + (num++) + "个环路,路径为: " + path);
}
}else{
System.err.println("该图不存在存在环路!");
}
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e.getMessage());
}
}
测试结果(有bug,待修正...):
本算法的关键:
在有向无环图中,每条边都指向一个post值更小的顶点;
判断有向图是否有环路,只需判断是否有回边即可,再对这些回边的起始顶点进行DFS,找到起始顶点到终端顶点的路径即为环路的路径.
对于起始顶点进行DFS还未实现,故当一条回边被多个环路共同拥有时出错,方法正在寻找中...