检测有向图和无向图中的环

有向图和无向图的环检测

1.无向图

深度优先搜索:思想是,DFS过程,记录当前结点的父结点,如果某结点已经被访问过且不是当前结点的父结点,则存在环

并查集:如果两个结点存在一条边,判断两个结点的在并查集中的根结点是否相同,不相同则表明在不同的连通块,把他们union到一个连通块;

否则,在遍历过程中发现如果某条边的两个结点都在一个连通块,则表明存在环

package dfs;

//冗余连接是指root[x] == root[y]
class Solution684 {

    class Union{
        private int[] root;
        private int[] path;
        Union(int n){
            root = new int[n];
            path = new int[n];
            for(int i=0;i<n;i++){
                root[i] = i;
            }
        }
        public boolean union(int x,int y){
            int rootX = find(x);
            int rootY = find(y);
            if(rootX==rootY){
                return false;
            }
            if(path[rootX]<path[rootY]){        //将小树的根结点连接到大树的根结点
                root[rootX] = rootY;
                path[rootY] += 1;
            }else{
                root[rootY] = rootX;
                path[rootX] += 1;
            }
            return true;
        }

        public int find(int x){
            if(x != root[x]){
                root[x] = find(root[x]);
            }
            return root[x];
        }
    }

    public int[] findRedundantConnection(int[][] edges) {
        int n = edges.length;
        Union ufind = new Union(n+1);       //结点是从1开始的所以加1

        for(int[] edge:edges){
            if(!ufind.union(edge[0],edge[1])){
                return edge;
            }
        }
        return edges[0];
    }
}

public class lc684 {
    public static void main(String[] args) {
        int[][] edges = {{1,2},{1,3},{2,3}};
        Solution684 s = new Solution684();
        int[] res  = s.findRedundantConnection(edges);
        System.out.println(res[0]+" "+res[1]);
    }
}

/*
c++版本
class Solution {

    int u[2000];
public:
    vector<int> findRedundantConnection(vector<vector<int>>& edges) {
        for(int i=0;i<edges.size();i++){
            u[i] = i;
        }
        vector<int> result(2,0);
        for(int i=0;i<edges.size();i++){
            vector<int> edge = edges[i];
            int root_x = find(edge[0]);
            int root_y = find(edge[1]);

            if(root_x == root_y){
                result[0] = edge[0];
                result[1] = edge[1];
            }else{
                combine(edge[0],edge[1]);
            }
        }
        return result;
    }

    int find(int x){
        while(x!=u[x]){
            u[x] = u[u[x]];
            x = u[x];
        }
        return u[x];
    }

    void combine(int x,int y){
        u[find(x)] = find(y);
    }

};
* */

2.有向图

深度优先搜索:

设置一个visited数组表示结点被访问过,如果在遍历过程中发现被访问过,则可能存在环;也存在被访问过但没有环的情况,所以需要借助栈,每当遍历一个结点,将结点推至栈中,结点是后进先出的,也就是

如果环入口的结点会更靠近栈底,只有当结点被遍历过,且位于栈中,则判定有环;当该结点所有的邻居结点遍历完成,或者退回至该结点,将该结点出栈,这样做的目的是为了保证防止出现假的环

package dfs;


import java.util.ArrayList;
import java.util.List;


/*
*       0  -   3
*     /  \  \  |
*    1 -  2   4
* */

class Solution207 {
    private List<Integer>[] g_list;

    //思路,用stack保存哪些结点在栈上,如果同时在栈上且被访问过,则有环

    public boolean canFinish(int numCourses, int[][] prerequisites) {
        boolean[] visited = new boolean[numCourses];
        boolean[] stack = new boolean[numCourses];
        g_list = new ArrayList[numCourses];
        //构建邻接表
        for(int i=0;i<numCourses;i++){
            g_list[i] = new ArrayList<>();
        }
        for(int[] z : prerequisites){
            g_list[z[0]].add(z[1]);
        }

        for(int i=0;i<numCourses;i++){
            if(!visited[i]){
                if(dfs(i,visited,stack)){
                    return false;
                }
            }
        }
        return true;
    }
    private boolean dfs(int v,boolean[] visited,boolean[] stack){
        visited[v] = true;
        stack[v] = true;
        for(int i : g_list[v]){
            if(visited[i] && stack[i]){
                return true;
            }
            if(visited[i]) {
                continue;
            }
            if(dfs(i,visited,stack)){
                return true;
            }
        }
        stack[v] = false;
        return false;
    }
}

public class lc207 {
    public static void main(String[] args) {
        int numCourses = 2;
        int[][] prerequisites = new int[][]{{1,0}};
        Solution207 s = new Solution207();
        boolean res = s.canFinish(numCourses,prerequisites);
        System.out.println(res);
    }


}

使用拓扑排序,

拓扑排序的主要步骤

(1)将入度为0的节点入队列

(2)每次弹出队列的头元素

(3)遍历该节点出发的变,并将重点节点的入度减1

(4)如果节点的入度为0, 则加入队列

(5)重复(2)(3)(4)

ArrayList<ArrayList<Integer>> graph ;
int[] degree = new int[5001];

public boolean toposort(int n){
    Queue<Integer> q = new LinkedList<>();
    //将所有入度为0的点如队列
    for(int i=0;i<n;i++){
        if(degree[i]==0){
            q.offer(i);
        }
    }
    //开始拓扑排序
    while(!q.isEmpty()){
        //取入度为0的点
        int cur = q.poll();
        //删除其与其他顶点的连边,同时这些顶点入度减一
        for(int x:graph.get(cur)){
            degree[x]--;
            //判断顶点的度是否为0,为0则加入队列
            if(degree[x]==0){
                q.offer(x);
            }
        }
    }
    //判断是否还有结点度不为0, 如果有则存在环
    for (int i=0;i<n;i++){
        if(degree[i] != 0){
            return true;
        }
    }
    return false;
}

//https://leetcode-cn.com/problems/circular-array-loop/solution/gtalgorithm-tuo-bu-zai-xian-by-gtalgorit-ah46/

java 队列

Queue<Integer> q = new LinkedList();

添加元素至队尾, q.offer(1)

取队列头部元素并删除队列头 q.poll()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值