第一次写博客,不太会用,话不多说 直接上代码 详细可以看注释,无向图判断是否存在环比有向图相对复杂一点需要判断访问的节点的临接表中的节点与父节点是否相同。

/**
 * @Description:判断无向图是否有环 深度优先遍历
 * 需要保存父节点
 * @Create 2020-04-03 21:04
 * @Email:1173748742@qq.com
 */
public class IsHaveLoop {
    public static void main(String[] args) {
        IsHaveLoop isHaveLoop = new IsHaveLoop();
        int[][] graph = {{0, 1}, {1, 2}, {2, 3}, {3, 4}, {2, 4}};
        int n = 5;
        boolean haveLoop = isHaveLoop.isHaveLoop(graph, n);
        System.out.println(haveLoop);
    }
    /**
     * @param graph 图的连接边
     * @param n     图的节点个数
     * @return 是否存在环
     */
    public boolean isHaveLoop(int[][] graph, int n) {
        //这段代码可要可不要,可以提前判断结果
//        if (graph.length >= n) {//当边数大于节点的个数时,必定存在环 自己可以画图推导
//            return true;
//        }
        //习惯上转换成临接表的形式
        List<Integer>[] adj = new ArrayList[n];
        for (int[] edg : graph) {
            int node1 = edg[0];
            int node2 = edg[1];
            if (adj[node1] == null) {
                adj[node1] = new ArrayList<>();
            }
            if (adj[node2] == null) {
                adj[node2] = new ArrayList<>();
            }
            adj[node1].add(node2);
            adj[node2].add(node1);
        }
        boolean[] visited = new boolean[n];//定义一个节点状态数组  判断是否访问过
        int[] a = {0};
        for (int i = 0; i < n; i++) {
            if (visited[i] == false) {//如果没有进行访问  则进行深度优先搜索回溯
                dfsCycle(adj, i, -1, visited, a);//引用传递  函数内部修改值后退出函数可见
//                System.out.println(a[0]);
                if (a[0] == 1) {//只要有一次i循环时存在环路那就直接提前返回,说明存在环
                    return true;
                }
            }
        }
        return a[0] == 1;
    }


    /**
     * @param adj     图的临接表
     * @param current 当前节点
     * @param parent  父节点
     * @param visited 判断是否访问
     * @param flag    是否存在环
     */
    private void dfsCycle(List<Integer>[] adj, int current, int parent, boolean[] visited, int[] flag) {
        visited[current] = true;//首先 访问当前节点   并进行标记
        List<Integer> list = adj[current];  //获取到当前节点能够到达的所有节点
        for (Integer can : list) {
            if (visited[can] == false) {//如果节点没有被访问过
                dfsCycle(adj, can, current, visited, flag);//当前节点就是父节点,循环的节点就是子节点
            } else if (can != parent) {// 在节点被访问过的情况下 如果该节点不等于父节点  ,说明有环
                flag[0] = 1;
            }
            //循环节点等于父节点的情况直接跳过,不用处理
        }
    }
}

更新2020-8-17

添加一个使用并查集的方法。如果没有了解过并查集的话可以推荐看看这个视频,灯神讲的真好。思想就是不断对两个边进行联合,如果能够联合,这说明这条边加上去不能使集合产生环,继续向后遍历,如果存在一条边在进行联合时,没办法联合,说明会产生环。下边贴一下java代码。有的位置加了注释应该可以理解。

package com.atschool.algorithm;

import java.util.Arrays;

/**
 * @Description:查看无向图中有没有环
 * @Create 2020-08-17 14:42
 * @Email:1173748742@qq.com
 */
public class DisjoinSet {

    private int[] parent;
    private int[] rank;

    /**
     * 这里要求节点编号从0开始进行编号
     * 如果不是的话就不可以用数组来作为map,可能需要用两个HashMap才行
     *
     * @param edges 边
     * @param n     节点个数
     * @return
     */
    public boolean detecteCycle(int[][] edges, int n) {
        parent = new int[n];
        Arrays.fill(parent, -1);
        rank = new int[n];
        for (int i = 0; i < edges.length; i++) {
            int x = edges[i][0];
            int y = edges[i][1];
            if (!union(x, y)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 连接节点
     *
     * @param x
     * @param y
     * @return
     */
    private boolean union(int x, int y) {
        int x_root = find_root(x);
        int y_root = find_root(y);
        if (x_root == y_root) {
            return false;   // 连接失败  // 说明有环
        } else {
            // 可以进行连接
            // 下边的判断是为了降低树的高度  一个小优化
            if (rank[x_root] > rank[y_root]) {
                parent[y_root] = x_root;
            } else if (rank[x_root] < rank[y_root]) {
                parent[x_root] = y_root;
            } else {
                parent[x_root] = y_root;
                rank[y_root]++;
            }
            return true;
        }
    }

    /**
     * 找到根节点函数
     *
     * @param x
     * @return
     */
    private int find_root(int x) {
        int x_root = x;
        while (parent[x_root] != -1) {
            x_root = parent[x_root];
        }
        return x_root;
    }


    public static void main(String[] args) {
        DisjoinSet disjoinSet = new DisjoinSet();
        int[][] edges = {{0, 1}, {1, 2}, {1, 3}, {3, 4}, {2, 4}, {4, 5}}; // 有环
//        int[][] edges = {{0, 1}, {1, 2}, {1, 3}, {3, 4}, {4, 5}};  无环
        boolean b = disjoinSet.detecteCycle(edges, 6);
        if (b) {
            System.out.println("detected cycle !");
        } else {
            System.out.println("no cycle!");
        }
    }
}