题目
https://leetcode-cn.com/problems/course-schedule/
你这个学期必须选修 numCourse 门课程,记为 0 到 numCourse-1 。
在选修某些课程之前需要一些先修课程。 例如,想要学习课程 0 ,你需要先完成课程 1 ,我们用一个匹配来表示他们:[0,1]
给定课程总量以及它们的先决条件,请你判断是否可能完成所有课程的学习?
示例 1:
输入: 2, [[1,0]]
输出: true
解释: 总共有 2 门课程。学习课程 1 之前,你需要完成课程 0。所以这是可能的。
示例 2:
输入: 2, [[1,0],[0,1]]
输出: false
解释: 总共有 2 门课程。学习课程 1 之前,你需要先完成课程 0;并且学习课程 0 之前,你还应先完成课程 1。这是不可能的。
提示:
输入的先决条件是由 边缘列表 表示的图形,而不是 邻接矩阵 。详情请参见图的表示法。
你可以假定输入的先决条件中没有重复的边。
1 <= numCourses <= 10^5
解题思路
本题其实就是在图中找环。
- Topological Sorting 拓扑排序
- 先根据课程先后顺序构建图
- 初始化课程状态数组,初始化为未访问
- 状态分3种:未访问,访问中,已访问
- 遍历图的全部节点,在每个节点上进行DFS
- DFS进来后查看状态数组,
- 如果当前节点状态为访问中,说明有环,返回False
- 如果当前节点状态为已访问,说明不需要继续了,返回True
- 剩余状态就是未访问,
- 先将本节点状态设置为访问中
- 如果本节点还有后续节点,则遍历后续节点,在每个节点上DFS并检查返回值
- 最后将本节点状态置为已访问
- 如果有环,上面的流程就会返回False,否则返回True
代码
class Solution:
def canFinish(self, numCourses: int, prerequisites: List[List[int]]) -> bool:
# - build graph
g = defaultdict(list)
for a,b in prerequisites:
g[b].append(a)
# - course status
# - 0: unvisited, 1: visiting, 2: visited
status_list = [0] * numCourses
# - dfs
def dfs(course):
# - if the course is in visiting, means loop found
if status_list[course] == 1: return False
# - if the course is visited, it is ok
if status_list[course] == 2: return True
# - set status as visiting
status_list[course] = 1
# - traverse all next courses if has them
if course in g:
for c in g[course]:
if not dfs(c): return False
# - set status as visited
status_list[course] = 2
return True
# - check all courses
for course in g:
if not dfs(course):
return False
return True