有向图是否有环 BFS 和 DFS

  LeetCode 207. Course Schedule

There are a total of n courses you have to take, labeled from 0 to n - 1.

Some courses may have prerequisites, for example to take course 0 you have to first take course 1, which is expressed as a pair: [0,1]

Given the total number of courses and a list of prerequisite pairs, is it possible for you to finish all courses?

For example:

2, [[1,0]]
There are a total of 2 courses to take. To take course 1 you should have finished course 0. So it is possible.

2, [[1,0],[0,1]]
There are a total of 2 courses to take. To take course 1 you should have finished course 0, and to take course 0 you should also have finished course 1. So it is impossible.

Note:
The input prerequisites is a graph represented by a list of edges, not adjacency matrices. Read more about how a graph is represented.
You may assume that there are no duplicate edges in the input prerequisites.
click to show more hints.

  经过分析,这道题相当于判断所给有向图中是否有环。其所给图表示方法为邻接表,通常使用DFS或BFS遍历图,需要图以邻接矩阵的形式表示。因此此题需要先把邻接表表示转变为“类”邻接矩阵表示。再使用DFS或BFS遍历,若遍历到重复的顶点,则说明有环。
  Java代码如下:

//BFS
public class Solution {
    public boolean canFinish(int numCourses, int[][] prerequisites) {
        ArrayList[] graph = new ArrayList[numCourses];
        int[] degree = new int[numCourses];//记录顶点的入度
        Queue queue = new LinkedList();
        int count=0;//每有一门课程可以修习,count++

        for(int i=0;i<numCourses;i++)
            graph[i] = new ArrayList();

        //邻接表转邻接矩阵
        for(int i=0; i<prerequisites.length;i++){
            degree[prerequisites[i][1]]++;//入度+1
            graph[prerequisites[i][0]].add(prerequisites[i][1]);//相当于邻接矩阵的一行,从顶点prerequisites[i][0]出,顶点prerequisites[i][1]入
        }

        //对于入度为0的顶点,说明无需先修课程,可以被选作为图遍历开始的顶点,入队列
        for(int i=0; i<degree.length;i++){
            if(degree[i] == 0){
                queue.add(i);
                count++;
            }
        }

        while(queue.size() != 0){
            int course = (int)queue.poll();

            //对于课程course,若无后续课程,即也无出度,则graph[course].size()为0,直接跳过。
            //若有后续课程,则graph[course].size()>0。将其后续课程graph[course].get(i)的入度减1。若后续课程的前续课程全部修完,则入度degree减为0,后续课程可以进行修习count++。
            for(int i=0; i<graph[course].size();i++){
                int pointer = (int)graph[course].get(i);
                degree[pointer]--;
                if(degree[pointer] == 0){
                    queue.add(pointer);
                    count++;
                }
            }
        }
        if(count == numCourses)//可以修习的课程数量=全部课程数量
            return true;
        else    
            return false;
    }
}

//DFS
public class Solution {
        public boolean canFinish(int numCourses, int[][] prerequisites) {
            ArrayList[] graph = new ArrayList[numCourses];
            for(int i=0;i<numCourses;i++)
                graph[i] = new ArrayList();

            boolean[] visited = new boolean[numCourses];//默认为false,表示顶点未遍历
            //邻接表转邻接矩阵
            for(int i=0; i<prerequisites.length;i++){
                graph[prerequisites[i][1]].add(prerequisites[i][0]);
            }

            //从每一个顶点出发,都不会形成环,说明才无环
            for(int i=0; i<numCourses; i++){
                if(!dfs(graph,visited,i))
                    return false;
            }
            return true;
        }

        private boolean dfs(ArrayList[] graph, boolean[] visited, int course){
            if(visited[course])
                return false;//所遍历的顶点已经被遍历过,说明有环
            else
                visited[course] = true;;

            //遍历该节点指向的其他节点,若后代节点中存在该节点,则表示有环
            for(int i=0; i<graph[course].size();i++){
                if(!dfs(graph,visited,(int)graph[course].get(i)))
                    return false;
            }
            visited[course] = false;//重置为未遍历状态,供下一次变换初始节点遍历使用
            return true;
        }
    }

  一般图的BFS和DFS可以借鉴此处①或者这里②

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值