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
.
[0, 1]
, indicates that to take course 0
you have to first take course 1
.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 <= 2000
0 <= prerequisites.length <= 5000
prerequisites[i].length == 2
0 <= ai, bi < numCourses
This problem asks whether it's possible to finish all courses given prerequisites. We can model this as a directed graph where courses are nodes and prerequisites are edges. A course a
has an edge to course b
if b
must be taken before a
. The problem then becomes determining if there's a cycle in this directed graph. A cycle indicates an impossible schedule because courses depend on each other circularly.
The most efficient solution uses topological sorting. Topological sorting arranges nodes in a directed acyclic graph (DAG) such that for every directed edge from node A to node B, node A appears before node B in the ordering. If a cycle exists, topological sorting is impossible.
Algorithm:
Graph Representation: Create an adjacency list g
to represent the course dependencies. g[i]
contains a list of courses that depend on course i
. Also, create an indeg
array to store the in-degree (number of incoming edges) of each course.
Initialization: Populate g
and indeg
using the prerequisites
array. The in-degree of a course represents the number of courses that must be taken before it.
Queue: Initialize a queue q
with all courses having an in-degree of 0 (courses with no prerequisites).
Topological Sort: Iterate while the queue is not empty:
i
.i
(courses in g[i]
).Cycle Detection: After the loop, if numCourses
is 0, it means all courses have been processed, implying no cycles. Otherwise, a cycle exists.
Time Complexity: O(V + E), where V is the number of courses (nodes) and E is the number of prerequisites (edges). This is because we visit each node and edge once.
Space Complexity: O(V + E), due to the adjacency list g
and indeg
array.
Code Examples (Python):
from collections import defaultdict, deque
def canFinish(numCourses, prerequisites):
"""
Determines if all courses can be finished given prerequisites.
Args:
numCourses: The total number of courses.
prerequisites: A list of prerequisites, where each element is a list [a, b] indicating that course a requires course b.
Returns:
True if all courses can be finished, False otherwise.
"""
graph = defaultdict(list)
in_degree = [0] * numCourses
for a, b in prerequisites:
graph[b].append(a) # Course 'a' depends on course 'b'
in_degree[a] += 1
queue = deque([i for i, degree in enumerate(in_degree) if degree == 0])
count = 0 # Count of processed courses
while queue:
course = queue.popleft()
count += 1
for neighbor in graph[course]:
in_degree[neighbor] -= 1
if in_degree[neighbor] == 0:
queue.append(neighbor)
return count == numCourses # if all courses were processed, no cycles
The code in other languages follows the same algorithm, only differing in syntax and data structure implementations. The core logic remains consistent across all languages presented.