题目:
你这个学期必须选修 numCourses 门课程,记为 0 到 numCourses - 1 。
在选修某些课程之前需要一些先修课程。 先修课程按数组 prerequisites 给出,其中 prerequisites[i] = [ai, bi] ,表示如果要学习课程 ai 则 必须 先学习课程 bi 。
- 例如,先修课程对
[0, 1]表示:想要学习课程0,你需要先完成课程1。
请你判断是否可能完成所有课程的学习?如果可以,返回 true ;否则,返回 false 。
算法:
方法一:DFS
本质上是判断DAG是否存在环,我们先构建邻接表,表明从preRequireCourse->curCourse的关系。然后对每一个课程进行遍历,用flag数组标识课程是否已经查找过了,本轮查找过了1,其他轮查找过了-1,未查找0
func canFinish(numCourses int, prerequisites [][]int) bool {
adjacency := make([][]int, numCourses)
flag := make([]int, numCourses)
for i := range prerequisites {
adjacency[prerequisites[i][1]] = append(adjacency[prerequisites[i][1]], prerequisites[i][0])
}
var dfs func(course int) bool
dfs = func(course int) bool {
if flag[course] == 1 {
return false
}
if flag[course] == -1 {
return true
}
flag[course] = 1
for j := range adjacency[course] {
if !dfs(adjacency[course][j]) {
return false
}
}
flag[course] = -1
return true
}
// 这里其实只遍历邻接表中存在的关系课程就可以,即adjacency中values数组长度大于0的元素。
for i := 0; i < numCourses; i ++ {
if !dfs(i) {
return false
}
}
return true
}
方法二:BFS
使用三个数据结构入度数组inDegree(保存每个顶点的入度),邻接表:adjency(表示前置课程和后续课程的依赖关系),队列queue(保存入度为0的顶点,队列中的课程意味着可以选择)。
初始化inDegree和adjency后,将入度为0的课程加入队列(选课),该课程选完之后 count++,后置课程的入度-1,如果课程的入度为0了,则加入队列queue。
最后比较count是否等于numCourses,即是否所有的可都可以选到。
func canFinish(numCourses int, prerequisites [][]int) bool {
// index前置依赖课程,value完成前置之后可选择的后续课程
adjacency := make([][]int, numCourses)
inDegree := make([]int, numCourses)
count := 0
// 初始化inDegree, 初始化adjacency
for _, req := range prerequisites {
adjacency[req[1]] = append(adjacency[req[1]], req[0])
inDegree[req[0]] ++
}
queue := make([]int, 0)
for course, inDegree := range inDegree {
if inDegree == 0 {
queue = append(queue, course)
count ++
}
}
// 入队列queue
for len(queue) != 0 {
preCourse := queue[0]
queue = queue[1:]
for _, afterCourse := range adjacency[preCourse] {
inDegree[afterCourse] --
if inDegree[afterCourse] == 0 {
queue = append(queue, afterCourse)
count ++
}
}
}
return count == numCourses
}
优雅!