Course Schedule
Problem
https://leetcode.com/problems/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] = [a:sub:`i`, b:sub:`i`]
indicates that you must take course b:sub:`i` first if you
want to take course a:sub:`i`.
For example, the pair
[0, 1], indicates that to take course0you 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.
Constraints:
1 <= numCourses <= 20000 <= prerequisites.length <= 5000prerequisites[i].length == 20 <= a:sub:`i`, b:sub:`i`< numCoursesAll the pairs prerequisites[i] are unique.
Pattern
Depth-First Search, Breadth-First Search, Graph Theory, Topological Sort
Approaches
Explanation
If the courses can be finished, we will be able to use topological sort to
produce a valid course path. First, convert the list of prerequisites
from an edge list into an adjacency list while also
tracking the in degree of each node in a dictionary in_degree (the
number of remaining prereqs). We can only start a course when all its
prereqs are finished, i.e. in-degree = 0. We add these to a queue. As we
take each course by popping from the queue, we reduce the in-degree of the
downstream courses by 1. If those courses reach and in-degree of 0, then we
add them to the queue.
If the set of taken courses contains all courses, then it is possible to finish the courses. Otherwise, it is not.
Code
from collections import defaultdict, deque
def canFinish(numCourses: int, prerequisites: list[list[int]]) -> bool:
"""Return whether it is possible to finish all courses given the list of
``prerequesites``.
"""
graph = defaultdict(list)
in_degrees = defaultdict(int)
for [a, b] in prerequisites:
graph[b].append(a)
graph[a]
in_degrees[a] += 1
in_degrees[b]
queue = deque()
for node, in_degree in in_degrees.items():
if in_degree == 0:
queue.append(node)
courses = set()
while queue:
node = queue.popleft()
courses.add(node)
for neighbor in graph[node]:
in_degrees[neighbor] -= 1
if in_degrees[neighbor] == 0:
queue.append(neighbor)
if len(courses) == len(graph):
return True
else:
return False
Test
>>> from course_schedule__topological_sort import canFinish
>>> canFinish(2, [[1, 0]])
True
>>> canFinish(2, [[1, 0], [0, 1]])
False
Complexity
\(V\) is the number of courses and \(E\) is the length of prerequisites.
Measure |
Complexity |
Notes |
|---|---|---|
Time |
\(O(V + E)\) |
every edge and vertex is visited once |
Auxiliary Space |
\(O(V + E)\) |
we create a graph dictionary which stores all the edges and a taken courses array which stores all the courses |
- course_schedule__topological_sort.canFinish(numCourses: int, prerequisites: list[list[int]]) bool
Return whether it is possible to finish all courses given the list of
prerequesites.