Course Schedule
There are a total of numCourses
courses you have to take, labeled from 0
to numCourses - 1
. You are given an array prerequisites
where prerequisites[i] = [ai, bi]
indicates that you must take course bi
first if you want to take course ai
.
- For example, the pair
[0, 1]
, indicates that to take course0
you have to first take course1
.
Return true
if you can finish all courses. Otherwise, return false
.
Example 1:
Input: numCourses = 2, prerequisites = [[1,0]]
Output: true
Explanation: There are a total of 2 courses to take.
To take course 1 you should have finished course 0. So it is possible.
Example 2:
Input: numCourses = 2, prerequisites = [[1,0],[0,1]]
Output: false
Explanation: There are a total of 2 courses to take.
To take course 1 you should have finished course 0, and to take course 0 you should also have finished course 1. So it is impossible.
Solution
In here we can use the same concept as Topological Sort (Kahn's algorithm).
class App:
def canFinish(self, numCourses: int, prerequisites: List[List[int]]) -> bool:
prerequisiteToCourse = defaultdict(lambda: [])
indegreeMap = defaultdict(lambda: 0)
for course, dependency in prerequisites:
prerequisiteToCourse[dependency].append(course)
indegreeMap[course] += 1
toStudy = [course for course in range(numCourses) if indegreeMap[course] == 0]
studied = set()
while toStudy:
currentCourse = toStudy.pop()
studied.add(currentCourse)
for nextCourse in prerequisiteToCourse[currentCourse]:
if nextCourse in studied:
# Cycle depdendency
return False
indegreeMap[nextCourse] -= 1
if indegreeMap[nextCourse] == 0:
toStudy.append(nextCourse)
return len(studied) == numCourses
- Time complexity: $O(|V| + |E|)$ — $|V|$ is the number of course and $|E|$ is the number of dependency
- Space complexity: $O(|V| + |E|)$ — $|V|$ is the number of course and $|E|$ is the number of depnedency
Note:
In here, for the visited
set we can either add it in the nextCourse
for loop or in the main loop is fine, we don't have to follow BFS Visited location. So the above code is correct or this following one is also correct:
# ...
toStudy = [course for course in range(numCourses) if indegreeMap[course] == 0]
studied = set(toStudy) # NOTE HERE
while toStudy:
currentCourse = toStudy.pop()
for nextCourse in prerequisiteToCourse[currentCourse]:
if nextCourse in studied:
# Cycle dependency
return False
indegreeMap[nextCourse] -= 1
if indegreeMap[nextCourse] == 0:
toStudy.append(nextCourse)
studied.add(nextCourse) # NOTE HERE
return len(studied) == numCourses
The reason why Graph Traversal > Common mistakes we add the visited
set there is because at some point in the future we might visit the one that we're meant to visit.
In this case it's a bit different, we're not keep adding the next course everytime, we only add if indegreeMap[nextCourse] == 0
, and that's guarding the other course to add the nextCourse
in the toStudy
queue.
Therefore, if we put the visited
when we take the course, that's fine as well because that will only happen once.