拓扑排序
leetcode上的题目:课程表
参考题解:https://leetcode-cn.com/problems/course-schedule/solution/course-schedule-tuo-bu-pai-xu-bfsdfsliang-chong-fa/
解题思路:
- 本题可约化为: 课程安排图是否是 有向无环图(DAG)。即课程间规定了前置条件,但不能构成任何环路,否则课程前置条件将不成立。
- 思路是通过 拓扑排序 判断此课程安排图是否是 有向无环图(DAG) 。 拓扑排序原理: 对 DAG 的顶点进行排序,使得对每一条有向边 (u, v),均有 u(在排序记录中)比 v 先出现。亦可理解为对某点 v 而言,只有当 v 的所有源点均出现了,v才能出现。
深度优先遍历
原理是通过 DFS 判断图中是否有环。
算法流程:
- 借助一个标志列表
flags
,用于判断每个节点i
(课程)的状态: - 对
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。
代码:
class Solution {
public boolean canFinish(int numCourses, int[][] prerequisites) {
List<List<Integer>> adjacency = new ArrayList<>();
for(int i = 0; i < numCourses; i++)
adjacency.add(new ArrayList<>());
int[] flags = new int[numCourses];
for(int[] cp : prerequisites)
adjacency.get(cp[1]).add(cp[0]);
for(int i = 0; i < numCourses; i++)
if(!dfs(adjacency, flags, i)) return false;
return true;
}
private boolean dfs(List<List<Integer>> adjacency, int[] flags, int i) {
//先判断再修改标志位
if(flags[i] == 1) return false;
//别的dfs路径访问过了,我不需要访问了
if(flags[i] == -1) return true;
//只有这个标志位是干净的,别人还没有动过,我才能标记为1,说明本次dfs我遍历过它
flags[i] = 1;
for(Integer j : adjacency.get(i))
if(!dfs(adjacency, flags, j)) return false;
//只有一次DFS完整结束了,才能执行到这一步,标记为-1,说明这条路没问题,再遇到不需要遍历了
flags[i] = -1;
return true;
}
}