1. 题目描述
你这个学期必须选修
numCourses
门课程,记为0
到numCourses - 1
。
在选修某些课程之前需要一些先修课程。 先修课程按数组
prerequisites
给出,其中prerequisites[i] = [ai, bi]
,表示如果要学习课程ai
则 必须 先学习课程bi
。
- 例如,先修课程对
[0, 1]
表示:想要学习课程0
,你需要先完成课程1
。
请你判断是否可能完成所有课程的学习?如果可以,返回true
;否则,返回false
输入:numCourses = 2, prerequisites = [[1,0]]
输出:true
解释:总共有 2 门课程。学习课程 1 之前,你需要完成课程 0 。这是可能的。
输入:numCourses = 2, prerequisites = [[1,0],[0,1]]
输出:false
解释:总共有 2 门课程。学习课程 1 之前,你需要先完成课程 0 ;并且学习课程 0 之前,你还应先完成课程 1 。这是不可能的。
2.解题思路
2.1 广度优先 根据入度拓补排序
- 统计课程安排图中每个节点的入度,生成 入度表
indegrees
。- 借助一个队列
queue
,将所有入度为0
的节点入队。- 当 queue 非空时,依次将队首节点出队,在课程安排图中删除此节点 pre:
- 并不是真正从邻接表中删除此节点
pre
,而是将此节点对应所有邻接节点cur
的入度-1
,即indegrees[cur] -= 1
。
- 当入度
-1
后邻接节点cur
的入度为0
,说明cur
所有的前驱节点已经被 “删除”,此时将cur
入队。
- 在每次
pre
出队时,执行numCourses--
;
- 若整个课程安排图是有向无环图(即可以安排),则所有节点一定都入队并出队过,即完成拓扑排序。换个角度说,若课程安排图中存在环,一定有节点的入度始终不为
0
。
- 因此,拓扑排序出队次数等于课程个数,返回
numCourses == 0
判断课程是否可以成功安排。
时间复杂度
O(N + M)
空间复杂度
O(N + M)
2.2 深度优先 判断图中是否有环
1.借助一个标志列表 flags,用于判断每个节点 i (课程)的状态:
- 未被 DFS 访问:i == 0;
- 已被其他节点启动的 DFS 访问:i == -1;
- 已被当前节点启动的 DFS 访问:i == 1。
2.对 numCourses 个节点依次执行 DFS,判断每个节点起步 DFS 是否存在环,若存在环直接返回 False。
3.当前节点所有邻接节点已被遍历,并没有发现环,则将当前节点 flag 置为 -1 并返回 True。
若整个图 DFS 结束并未发现环,返回 True。
3. 代码实现
一般这种问题上来先生成邻连表, 后续问题处理起来比较方便
// 1. 广度优先搜索 入度表
func canFinish(numCourses int, prerequisites [][]int) bool {
// 邻连表
adjacency := make([][]int, numCourses)
// 入度表
indegrees := make([]int, numCourses)
for _, prerequisite := range prerequisites {
indegrees[prerequisite[1]]++
adjacency[prerequisite[1]] = append(adjacency[prerequisite[1]], prerequisite[0])
}
queue := []int{}
for i := 0; i < numCourses; i++ {
if indegrees[i] == 0 {
queue = append(queue, i)
}
}
for len(queue) > 0 {
node := queue[0] // 出队
queue = queue[1:]
numCourses--
// 将队头节点相连的节点的入度-1
for i := 0; i < len(adjacency[node]); i++ {
indegrees[adjacency[node][i]]--
// 入度为0加入队列
if indegrees[adjacency[node][i]] == 0 {
queue = append(queue, adjacency[node][i])
}
}
}
return numCourses == 0
}
// 2. 深度优先搜索 判断图中是否有环
func canFinish(numCourses int, prerequisites [][]int) bool {
adjacency := make([][]int, numCourses)
flag := make([]int, numCourses)
for _, prerequisite := range prerequisites {
adjacency[prerequisite[1]] = append(adjacency[prerequisite[1]], prerequisite[0])
}
for i := 0; i < numCourses; i++ {
if !dfs(adjacency, flag, i) {
return false
}
}
return true
}
func dfs(adjacency [][]int, flag []int, i int) bool {
if flag[i] == -1 {
return true
}
if flag[i] == 1 {
return false
}
flag[i] = 1
for j := 0; j < len(adjacency[i]); j++ {
if !dfs(adjacency, flag, adjacency[i][j]) {
return false
}
}
flag[i] = -1
return true
}