Course Schedule II⚓︎
Description⚓︎
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_i, b_i]
indicates that you must take course b_i
first if you want to take course a_i
.
- For example, the pair
[0, 1]
, indicates that to take course0
you have to first take course1
.
Return the ordering of courses you should take to finish all courses. If there are many valid answers, return any of them. If it is impossible to finish all courses, return an empty array.
Example 1:
- Input:
numCourses = 2, prerequisites = [[1,0]]
- Output:
[0,1]
- Explanation:
There are a total of 2 courses to take. To take course 1 you should have finished course 0. So the correct course order is [0,1].
Example 2:
- Input:
numCourses = 4, prerequisites = [[1,0],[2,0],[3,1],[3,2]]
- Output:
[0,2,1,3]
- Explanation:
There are a total of 4 courses to take. To take course 3 you should have finished both courses 1 and 2. Both courses 1 and 2 should be taken after you finished course 0. So one correct course order is [0,1,2,3]. Another correct ordering is [0,2,1,3].
Example 3:
- Input:
numCourses = 1, prerequisites = []
- Output:
[0]
Constraints:
1 <= numCourses <= 2000
0 <= prerequisites.length <= numCourses * (numCourses - 1)
prerequisites[i].length == 2
0 <= a_i, b_i < numCourses
a_i != b_i
- All the pairs
[a_i, b_i]
are distinct.
Solution⚓︎
Way 1⚓︎
This problem is a classic example of using topological sort in a directed graph, where courses are represented as nodes and prerequisites as directed edges. The algorithm aims to find a linear ordering of the vertices such that for every directed edge \(U,V\) from vertex \(U\) to vertex \(V\), \(U\) comes before \(V\) in the ordering.
Algorithm Steps:
-
Representation of Graph: The graph is represented using an adjacency list (
edges
), whereedges[i]
contains a list of courses that can be taken after completing coursei
. Additionally, aninDegree
array is used to keep track of the number of prerequisites (incoming edges) for each course. -
Building the Graph: The prerequisites list is iterated to build the graph and update the in-degree of each node. For each pair
[a, b]
inprerequisites
, an edge fromb
toa
is added to the graph, indicating thatb
is a prerequisite ofa
. Consequently, the in-degree ofa
is incremented. -
Initializing the Queue: A queue is used to keep track of all courses (nodes) that have no prerequisites (in-degree of 0). These courses are safe to take first, as they do not depend on the completion of any other courses.
-
Processing the Queue: While the queue is not empty, the algorithm repeatedly removes a course from the queue and adds it to the result list (
res
). It then decreases the in-degree of all its dependent courses by 1, because taking the current course satisfies one of their prerequisites. If any of these courses now have no remaining prerequisites (in-degree becomes 0), they are added to the queue. -
Checking for a Possible Order: After processing all courses, if the size of the result list is less than
numCourses
, it means not all courses can be completed (due to a cycle or unsatisfiable prerequisites), and an empty array is returned. Otherwise, the listres
provides a valid order in which all courses can be finished.
Complexity Analysis:
- Time Complexity: \(O(V + E)\), where \(V\) is the number of courses (
numCourses
), and \(E\) is the number of prerequisites. Building the graph requires \(O(E)\) time, and each node and edge is processed exactly once in the topological sort. - Space Complexity Analysis: \(O(V + E)\). The adjacency list (
edges
) stores all edges, and the in-degree array, queue, and result list collectively store information for all vertices. Therefore, the space complexity is proportional to the total number of courses and prerequisites.
Pseudocode:
Way 2⚓︎
Class Members:
h
,e
,ne
: Arrays used to represent the graph.idx
: A counter used to keep track of the number of edges added to the graph.inDegree
: Array to track the number of incoming edges (prerequisites) for each course.que
: Array used as a queue to store courses with no current prerequisites.hh
,tt
: Head and tail pointers for the queue represented byque
.
Graph Representation:
This solution uses a more space-efficient method for representing graphs, especially sparse graphs, known as the adjacency list using arrays:
h[a]
stores the head of the linked list for vertexa
, pointing to its first outgoing edge.e[i]
represents the endpoint (or target node) of thei
-th edge.ne[i]
points to the next edge in the list originating from the same vertex as thei
-th edge, creating a chain of all outgoing edges from a vertex.- This representation is efficient in terms of memory and is particularly useful in graph algorithms where edge lookups and additions are frequent.