资讯专栏INFORMATION COLUMN

[基本算法] Detect Cycle in Directed/Undirected Graph 有

ymyang / 1576人阅读

摘要:不在的话,表示不构成环,我们应该合并这两个集合因为加上这条边,两个集合就被连起来了,合并成了一个集合注意如果想要复杂度为必须用路径压缩。并查集写法参考注意方法用找的老大哥,合并的是的老大哥。

Detect Cycle in Directed Graph 有向图找环

Given n nodes labeled from 0 to n - 1 and a list of directed edges (each edge is a pair of nodes), write a function to check whether the graph contains a cycle. if edges = [0, 1], [1, 2], [0, 2]], it means 0 -> 1, 1 ->2, 0 -> 2. edges[i is parent, edgesi is child.

For example:

Given n = 3 and edges = [[0, 1], [1, 2], [0, 2], return true.
Given n = 3 and edges = [[0, 1], [1, 2], [2, 0], return false.

黑白灰DFS大法 复杂度

O( V + E ) 时间 O(V) 空间

思路

什么是有向图有环:只要从a可以到a,经过的边的个数大于等于1

做法:
维护三个点的集合:

白点集合,里面放还没explore的点

灰点集合,里面放正在explore的点,当前的灰点们表示一条正在explore的路径,这个路径上的每个点都是灰的

黑点集合,里面放已经explore的点且这些点不构成环

如果我将要的访问的点是黑点,我是否需要访问他?
答:不需要,一个点是黑的表示这个点的所有孩子(邻居)都explore过了,且没发现环,且这个点的所有孩子必定也全是黑的。

何时把一个点由灰变黑?
答:当这个点的所有孩子都访问完(都已变黑)了且没有发现环

如果我将要访问的点是个灰点,说明什么?
答:发现了环。假设explore到了cur点,cur点为灰色,此时所有其他的灰色点必定都是我的祖先,因为他们都是当前explore的路径上的点,cur在最战线的最前方explore,如果cur点在explore的时候发现自己的的孩子(邻居)有一个灰色,表示下面这个点即是我的祖先也是我的孩子,说明从cur可以走到cur自己,即出现了环。

注意

无向图和有向图很不一样,不是一回事

代码

核心代码:

public boolean hasCycleDirectedGraph(int n, int[][] edges) {//前指后
        HashSet black = new HashSet();
        HashSet white = new HashSet();
        HashSet gray = new HashSet();
        List> adjList = new ArrayList>();
        for (int i = 0; i < n; ++i) {
            white.add(new Integer(i));
            adjList.add(new ArrayList()); 
        }
            
        for (int[] edge: edges) {
            adjList.get(edge[0]).add(new Integer(edge[1]));
        }
        for (int i = 0; i < n; i++) {
            if (white.contains(i)) {
                if (hasCycle(i, white, gray, black, adjList))
                    return true;
            }
        }
        return false;
    }
    private boolean hasCycle(Integer vertex, HashSet white, HashSet gray, HashSet black, List> adjList) {
        white.remove(vertex);
        gray.add(vertex);  // current vertex is being visited
        for (Integer succ: adjList.get(vertex)) {  // successors of current vertex
            if (white.contains(succ)) {
                if (hasCycle(succ, white, gray, black, adjList))
                    return true;
            }
            else if (gray.contains(succ)) {
                return true;
            }
            else if (black.contains(succ)) {
                continue;
            }        
        }
        gray.remove(vertex);
        black.add(vertex);
        return false;
    }

测试代码:

public class Solution {
    public static void main(String[] args) {
        Solution so = new Solution();
        int[][] edges = {{0, 1}, {1, 2}, {2, 0}};
        boolean result = so.hasCycleDirectedGraph(3, edges);
        System.out.println(result);
    }
}
Detect Cycle in Undirected Graph 无向图找环

Given n nodes labeled from 0 to n - 1 and a list of undirected edges (each edge is a pair of nodes), write a function to check whether the graph contains a cycle.

For example:

Given n = 5 and edges = [[0, 1], [0, 2], [0, 3], [1, 4]], return false.
Given n = 5 and edges = [[0, 1], [1, 2], [2, 3], [1, 3], [1, 4]], return true.

并查集大法 复杂度

O( V + E ) 时间 O(V) 空间

思路

什么是无向图有环:只要从a可以到a,路径中每个边只用一次

数据结构:
并查集:
规定集合(即一个连通分量)应该满足的property:里面的任意两点有且只有一条路径可达。
最开始的时候n个点有n个连通分量,即n个集合。

做法:

想象一开始这个图只有顶点,没有边,我们来一条一条的添加边。
每遇到一条边,判断这边的两个端点是否在同一个集合里?

在的话,表示有环:因为两个点在一个集合里就表示这两个点已经有一条路径了,现在再加一条路径,必然构成环。

不在的话,表示不构成环,我们应该合并这两个集合:因为加上这条边,两个集合就被连起来了,合并成了一个集合

注意

如果想要复杂度为O(V+E),必须用路径压缩。
并查集写法参考
注意union(p,q)方法:用find()找p,q的老大哥,合并的是p,q的老大哥。

代码

UnionFind Utility(非常intuitive,应该练习在5分钟内写完):

class UnionFind {
    private int[] father;
    private int count;
    public UnionFind(int n) {
        father = new int[n];
        count = n;
        for (int i = 0; i < n; i++){
            father[i] = i;
        }
    }
    public int count() {
        return this.count;
    }
    public int find(int p) {
        int root = father[p];
        while (root != father[root])
            root = father[root];
        //as long as we get here, root is the final dad
        while (p != root) {
            int tmp = father[p];
            father[p] = root;
            p = tmp;
        }
        return root;
    }
    public void union(int p, int q) {
        int fatherP = find(p);
        int fatherQ = find(q);
        if (fatherP != fatherQ) {
            father[fatherP] = fatherQ;
            count--;
        }
    }
}

主程序

public class Solution {
    public boolean validTree(int n, int[][] edges) {
        UnionFind uf = new UnionFind(n);
        for (int[] edge : edges){
            int p = edge[0];
            int q = edge[1];
            if (uf.find(p) == uf.find(q))
                return false;
            else
                uf.union(p,q);
        }
        return uf.count() == 1;
    }
}
DFS/BFS法 复杂度

O( V + E ) 时间 O(V) 空间

思路

无向图找环和有向图找环本质上完全不同。
有向图找环需要三种颜色。
无向图找环只需要两种颜色,就是访问过的和没访问的。

dfs过程中如果碰到访问过的节点(当然这个节点不能是来时的节点),就是有环。

注意

Integer的比较问题

代码
public class Solution {
    public boolean validTree(int n, int[][] edges) {
        HashSet visited = new HashSet();
        List> adjList = new ArrayList>();
        for (int i=0; i()); 
        for (int[] edge: edges) {
            adjList.get(edge[0]).add(edge[1]);
            adjList.get(edge[1]).add(edge[0]);
        }
        if (hasCycle(-1, 0, visited, adjList))  // has cycle?
            return false;   
        if (visited.size() != n) // is all connected?
            return false;   
        return true;
    }

    private boolean hasCycle(Integer pred, Integer vertex, HashSet visited, List> adjList) {
        visited.add(vertex);  // current vertex is being visited
        for (Integer succ: adjList.get(vertex)) {  // successors of current vertex
            if (!succ.equals(pred)) {  // exclude current vertex"s predecessor
                if (visited.contains(succ)) { 
                    return true; // back edge/loop detected!
                }  
                else  {
                    if (hasCycle(vertex, succ, visited, adjList)) { 
                        return true; 
                    }
                }
            }
        }
        return false;
    }
}

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/64794.html

相关文章

  • Graph: Detect cycle

    摘要:无向图对于无向图,需要记录一个来判断这是不是无向图两个之间的连接。同一层的节点会出现相连的情况。如果同一层的这个节点是等待访问的,说明这两个节点之间有连接,所以有环的出现。有向图不需要记录 Graph: Detect Cycle Detect if any cycle exists in the graph. 无向图 0 - 1 | / 2 对于无向图,需要记录一个previous ...

    eechen 评论0 收藏0
  • 算法(第4版) Chapter 4.1 无向图

    摘要:边仅由两个顶点连接,并且没有方向的图称为无向图。用分隔符当前为空格,也可以是分号等分隔。深度优先算法最简搜索起点构造函数找到与起点连通的其他顶点。路径构造函数接收一个顶点,计算到与连通的每个顶点之间的路径。 Algorithms Fourth EditionWritten By Robert Sedgewick & Kevin WayneTranslated By 谢路云Chapter...

    kamushin233 评论0 收藏0
  • 算法(第4版) Chapter 4 练习题 答案

    摘要:离心率计算题目释义计算点的离心率,图的直径,半径,中心计算图的围长定义点的离心率图中任意一点,的离心率是图中其他点到的所有最短路径中最大值。图的中心图中离心率长度等于半径的点。改动离心率计算,在遍历中增加的赋值即可。 离心率计算 4.1.16 The eccentricity of a vertex v is the the length of the shortest path fr...

    13651657101 评论0 收藏0
  • Graph: Topological Sort

    Graph: Topological Sort 利用和detect cycle类似的思路, 用dfs + recursion解决。并且一定时一个有向图。 Stack stack = new Stack(); // 0:unvisit, 1:visited, 2:visiting public boolean topologicalSort(Node node) { if(node.stat...

    ShevaKuilin 评论0 收藏0
  • 【Change Detection系列一】$digest 在Angular新版本中重生

    摘要:感谢您的阅读如果喜欢这篇文章请点赞。它对我意义重大,它能帮助其他人看到这篇文章。对于更高级的文章,你可以在或上跟随我。 I’ve worked with Angular.js for a few years and despite the widespread criticism I think this is a fantastic framework. I’ve started w...

    legendaryedu 评论0 收藏0

发表评论

0条评论

ymyang

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<