在一个无向图中找环

目录索引:畅游面试中算法题集锦

在一个无向图中找环

PART1:DFS

请添加图片描述

Approach:

从每个未访问的节点运行 DFS。深度优先遍历可用于检测图中的循环。连通图的 DFS 生成一棵树。仅当图中存在回边Back Edge:从一个顶点指向其祖先顶点的边时,图中才存在环。回边Back Edge是将节点连接到自身(自循环)或其在 DFS 生成的树中的祖先之一的边。
要找到其任何祖先的回边Back Edge,保留一个访问过的数组,如果任何访问过的节点都有回边Back Edge,则存在一个循环并返回 true。

Algorithm:
  1. 使用给定数量的边和顶点创建图。
  2. 创建一个具有当前索引或顶点、访问过的数组和父节点的递归函数。
  3. 将当前节点标记为visited。
  4. 找出所有没有访问过且与当前节点相邻的顶点。递归调用这些顶点的函数,如果递归函数返回true返回true。
  5. 如果相邻节点不是父节点并且已经访问过,则返回 true。
  6. 创建一个包装类,为所有顶点调用递归函数,如果任何函数返回 true,则返回 true。
  7. 否则,如果对于所有顶点,函数返回 false 返回 false。

请添加图片描述

下图说明的是code中的g1图,无向图找环的过程

请添加图片描述

Code:
static class Graph {
    private int V;
    private LinkedList<Integer>[] adj;

    public Graph(int V) {
        this.V = V;
        adj = new LinkedList[V];
        for (int i = 0; i < V; i++) {
            adj[i] = new LinkedList<>();
        }
    }

    private void addEdge(int u, int v) {
        adj[u].add(v);
        adj[v].add(u);
    }

    /**
     * 从u出发,visited[] 标记节点是否访问过,parent表示父亲节点
     * 是否存在一个环
     */
    public boolean isCyclicUtil(int u, boolean[] visited, int parent) {
        visited[u] = true;//标记当前节点被访问过
        for (int v : adj[u]) {//遍历u的相邻节点
            if (!visited[v]) {//第一次访问v
                if (isCyclicUtil(v, visited, u)) return true;
            } else if (v != parent) return true;//相邻节点不是父节点且被访问过
        }
        return false;
    }

    public boolean isCyclic() {
        boolean[] visited = new boolean[V];
        for (int u = 0; u < V; u++) {
            if (!visited[u]) {
                if (isCyclicUtil(u, visited, -1)) return true;
            }
        }
        return false;
    }

    public static void main(String args[]) {
        Graph g1 = new Graph(5);
        g1.addEdge(1, 0);
        g1.addEdge(0, 2);
        g1.addEdge(2, 1);
        g1.addEdge(0, 3);
        g1.addEdge(3, 4);
        if (g1.isCyclic())
            System.out.println("Graph  contains cycle");
        else
            System.out.println("Graph  doesn 't contains cycle");
        Graph g2 = new Graph(3);
        g2.addEdge(0, 1);
        g2.addEdge(1, 2);
        if (g2.isCyclic())
            System.out.println("Graph  contains cycle");
        else
            System.out.println("Graph doesn 't contains cycle");
    }
    //Graph  contains cycle
	//Graph doesn 't contains cycle

PART2:Disjoint Set

带路径压缩的参考后文:一文掌握并查集算法

static class Graph {


    int V, E;//顶点和边的数量
    Edge[] edges;

    class Edge {
        int src;
        int dest;


    }

    //初始化
    public Graph(int v, int e) {
        V = v;
        E = e;
        edges = new Edge[E];
        for (int i = 0; i < E; i++) {
            edges[i] = new Edge();
        }
    }

    //返回i这个节点的子集
    private int find(int[] parents, int i) {
        if (parents[i] == -1) return i;
        return find(parents, parents[i]);
    }

    //合并两个子集,没有带路径压缩
    private void union(int[] parents, int x, int y) {
        parents[x] = y;
    }

    public int isCycle(Graph graph) {
        int[] parents = new int[graph.V];//分配V个节点的parents数组
        Arrays.fill(parents, -1);//初始化
        for (int i = 0; i < graph.E; i++) {
            Edge edge = graph.edges[i];
            int x = graph.find(parents, edge.src);
            int y = graph.find(parents, edge.dest);
            if (x == y) return 1;
            graph.union(parents, x, y);
        }
        return 0;
    }

    // Driver Method
    public static void main(String[] args) {
/* Let us create the following graph
0
| \
|  \
1---2 */
        int V = 3, E = 3;
        Graph graph = new Graph(V, E);
        // add edge 0-1
        graph.edges[0].src = 0;
        graph.edges[0].dest = 1;
        // add edge 1-2
        graph.edges[1].src = 1;
        graph.edges[1].dest = 2;
        // add edge 0-2
        graph.edges[2].src = 0;
        graph.edges[2].dest = 2;
        if (graph.isCycle(graph) == 1)
            System.out.println("graph contains cycle");
        else
            System.out.println("graph doesn't contain cycle");
    }

}

Reference

  • 3
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值