本题可约化为:课程安排图是不是 有向无环图(DAG)。即课程间规定了前置条件,但不能构成任何环路,不然课程前置条件将不成立。
思路是经过 拓扑排序 判断此课程安排图是不是 有向无环图(DAG)。
拓扑排序是对 DAG 的顶点进行排序,使得对每一条有向边 (u, v),均有 u(在排序记录中)比 v 先出现。亦可理解为对某点 v而言,只有当 v 的全部源点均出现了,v才能出现。
经过课程前置条件列表 prerequisites 能够获得课程安排图的 邻接矩阵 adjacency,如下两种方法都会用到邻接矩阵。算法
统计课程安排图中每一个节点的入度,生成 入度表 indegrees。
借助一个队列 queue,将全部入度为 00 的节点入队。
当 queue 非空时,依次将队首节点出队,在课程安排图中删除此节点 pre:
并非真正从邻接表中删除此节点 pre,而是将此节点对应全部邻接节点 cur 的入度 -1,即 indegrees[cur] -= 1。
当入度 -1后邻接节点 cur 的入度为 00,说明 cur 全部的前驱节点已经被 “删除”,此时将 cur 入队。
在每次 pre 出队时,执行 numCourses--;
若整个课程安排图是有向无环图(便可以安排),则全部节点必定都入队并出队过,即完成拓扑排序。换个角度说,若课程安排图中存在环,必定有节点的入度始终不为 0。
所以,拓扑排序出队次数等于课程个数,返回 numCourses == 0 判断课程是否能够成功安排。ui
时间复杂度 O(N + M),遍历一个图须要访问全部节点和全部临边,N 和 M分别为节点数量和临边数量;
空间复杂度 O(N),为创建邻接矩阵所需额外空间code
class Solution { public boolean canFinish(int numCourses, int[][] prerequisites) { int[] indegrees = new int[numCourses]; for(int[] cp : prerequisites) indegrees[cp[0]]++; LinkedList<Integer> queue = new LinkedList<>(); for(int i = 0; i < numCourses; i++){ if(indegrees[i] == 0) queue.addLast(i); } while(!queue.isEmpty()) { Integer pre = queue.removeFirst(); numCourses--; for(int[] req : prerequisites) { if(req[1] != pre) continue; if(--indegrees[req[0]] == 0) queue.add(req[0]); } } return numCourses == 0; }
}排序
算法流程(思路是经过 DFS 判断图中是否有环):
借助一个标志列表 flags,用于判断每一个节点 i (课程)的状态:
未被 DFS 访问:i == 0;
已被其余节点启动的DFS访问:i == -1;
已被当前节点启动的DFS访问:i == 1。
对 numCourses 个节点依次执行 DFS,判断每一个节点起步 DFS 是否存在环,若存在环直接返回 False。DFS 流程;
终止条件:
当 flag[i] == -1,说明当前访问节点已被其余节点启动的 DFS 访问,无需再重复搜索,直接返回 True。
当 flag[i] == 1,说明在本轮 DFS 搜索中节点 i 被第 2次访问,即 课程安排图有环,直接返回 False。
将当前访问节点 i 对应 flag[i] 置 1,即标记其被本轮 DFS 访问过;
递归访问当前节点 i 的全部邻接节点 j,当发现环直接返回 False;
当前节点全部邻接节点已被遍历,并无发现环,则将当前节点 flag 置为 -1 并返回 True。
若整个图 DFS 结束并未发现环,返回 True。
复杂度分析:
时间复杂度 O(N + M):遍历一个图须要访问全部节点和全部临边,N 和 M分别为节点数量和临边数量;
空间复杂度 O(N),为创建邻接矩阵所需额外空间。递归
class Solution { public boolean canFinish(int numCourses, int[][] prerequisites) { int[][] adjacency = new int[numCourses][numCourses]; int[] flags = new int[numCourses]; for(int[] cp : prerequisites) adjacency[cp[1]][cp[0]] = 1; for(int i = 0; i < numCourses; i++){ if(!dfs(adjacency, flags, i)) return false; } return true; } private boolean dfs(int[][] adjacency, int[] flags, int i) { if(flags[i] == 1) return false; if(flags[i] == -1) return true; flags[i] = 1; for(int j = 0; j < adjacency.length; j++) { if(adjacency[i][j] == 1 && !dfs(adjacency, flags, j)) return false; } flags[i] = -1; return true; }
}队列