LeetCode:课程表【207】

LeetCode:课程表【207】

题目描述

现在你总共有 n 门课需要选,记为 0 到 n-1

在选修某些课程之前需要一些先修课程。 例如,想要学习课程 0 ,你需要先完成课程 1 ,我们用一个匹配来表示他们: [0,1]

给定课程总量以及它们的先决条件,判断是否可能完成所有课程的学习?

示例 1:

输入: 2, [[1,0]] 
输出: true
解释: 总共有 2 门课程。学习课程 1 之前,你需要完成课程 0。所以这是可能的。

示例 2:

输入: 2, [[1,0],[0,1]]
输出: false
解释: 总共有 2 门课程。学习课程 1 之前,你需要先完成​课程 0;并且学习课程 0 之前,你还应先完成课程 1。这是不可能的。

说明:

  1. 输入的先决条件是由边缘列表表示的图形,而不是邻接矩阵。详情请参见图的表示法
  2. 你可以假定输入的先决条件中没有重复的边。

题目分析

  我们做这道题之前,需要先理解一个概念,就是拓扑排序。可以先参看这篇文章算法:拓扑排序

  如果我们已经理解了拓扑排序,那么这道题相对来说就比较简单了。这道题的核心点是判断有向图是否有环

  存在下面这种情况的就返回FALSE:

  

  拓扑排序算法如何判断是否有环?

    如上图的右半部分所示,首先黑色是节点的默认颜色,我们把访问到的节点标记为红色,在遇到出度为0的第一个节点时,我们将它标记为蓝色,最后所有的节点都会被染成蓝色,说明这是一个合法的拓扑图。

    但是,在有环路的情况下,无论如何都无法到达出度为0的节点,当所有的黑色节点都变成红色后,还没有出现一个蓝色,那说明就是有环。

    所以,我们把黑色表示为0,蓝色表示为1,红色表示为2,遇到黑色节点很正常,遇到蓝色节点也很正常(两者都是正常的拓扑排序环节),但是当我们遇到红色节点的时候说明遇到环路

    好了,说到这里,我觉得已经很清楚了,实现细节请看代码。

Java题解

class Solution {
     public boolean canFinish(int numCourses, int[][] prerequisites) {
        ArrayList<ArrayList<Integer>> graph = new ArrayList<>();
        for(int i=0;i<numCourses;i++)
            graph.add(new ArrayList<>());
        for(int i=0;i<prerequisites.length;i++)
        {
            int course = prerequisites[i][0];
            int pcourse = prerequisites[i][1];
            graph.get(course).add(pcourse);
        }
        int[] visited = new int[numCourses];
        for(int i=0;i<numCourses;i++)
            if(DFS(i,graph,visited))
                return false;
        return true;
    }

    public boolean DFS(int curr,ArrayList<ArrayList<Integer>> graph,int[] visited)
    {
        //递归结束条件
        if(visited[curr]==1)//这个节点已经被访问
            return true;
        if(visited[curr]==2)//这个节点没有被访问
            return false;
        
        //业务逻辑处理
        visited[curr]=1;//表示正在访问
        for(int id:graph.get(curr))
            if(DFS(id,graph,visited))
                return true;
        visited[curr]=2;//表示已经访问
        return false;
    }
}
原文地址:https://www.cnblogs.com/MrSaver/p/9996941.html