【力扣 Hot100】刷题日记
D6
这道题目做的时候也是没什么很好的思路,直到看到了灵神的题解。。。
课程表(原题链接)
题目主要内容截图
题目理解:
题目说学习课程1需要先完成课程2,那他们的关系为课程2 ——> 课程1,这是个有向线段,如果前置课程不止一个,那么这就会形成一个有向图。
能否完成所有课程的学习,也就是能通过所有路径走到底,但好像这么说并不明确,示例2也走到底了,但是完不成课程的学习。
为什么?
有向图中出现了环!!
解题思路:三色标记法
对于每个节点x,定义三种状态:
0:节点未被访问
1:节点正在被访问
2:节点已被访问过
为什么不用两种标记 1 和0?
如果用1和0表示访问过和未访问,如果出现了环,我们去查之前的元素,只会标记为1,再没有环的时候,之前访问过的元素也会是标记为1,我们区分不了,这个元素是不是在这次的搜索中访问了两次(也就是成环)
- 使用邻接表,记录前置课程和后置课程的关系,构成一个有向图
g
。 - 创建长度为
numCourses
的colors
表,记录当前课程的访问状态 - 遍历
colors
,如果colors[i] = 0
,那么dfs(i)
为true - 执行
dfs
:
- 将当前元素的访问状态标记为访问中(1)
- 遍历当前元素x的邻居元素y,如果邻居元素访问状态为1,说明出现了环。如果邻居元素访问状态为0,但是
dfs(y)
为true,说明出现了环 - 如果没找到环,将x访问状态标为2
class Solution {public boolean canFinish(int numCourses, int[][] prerequisites) {List<Integer>[] g = new ArrayList[numCourses];Arrays.setAll(g, i -> new ArrayList<>());for (int[] p : prerequisites) {g[p[1]].add(p[0]); //构建邻接表,p[1]表示前置课程,p[0]表示后置课程}int[] colors = new int[numCourses];for (int i = 0; i < numCourses; i++) { //遍历课程if(colors[i] == 0 && dfs(i, g, colors)){ //如果当前课程没被访问过,并且dfs结果是有环return false;}}return true;}private boolean dfs(int i, List<Integer>[] g, int[] colors){colors[i] = 1; //标记当前课程为正在访问for (Integer y : g[i]) { //当前课程的后置课程//如果当前课程的后置课程为正在访问,说明成环//如果邻居课程没被访问,但是对他的dfs出现了环if(colors[y] == 1 || (colors[y] == 0) && dfs(y, g, colors)){return true; //说明有环}}colors[i] = 2; //标记为访问过return false;}}