Course Schedule medium
Problem Statement
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]
means 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.
Steps to Solve
- Build the Graph: Represent the courses and their prerequisites as a directed graph. Each course is a node, and a directed edge from course
b
to coursea
indicates thatb
is a prerequisite fora
. - Detect Cycles: Use Depth-First Search (DFS) to detect cycles in the graph. A cycle indicates that there's a circular dependency between courses, making it impossible to complete all courses.
- Track Visited Nodes: Use two sets to track visited nodes during DFS:
visited
(nodes currently being visited) andcompleted
(nodes whose traversal is complete). A node in bothvisited
andcompleted
means there's no cycle involving that node. - Return Result: If DFS detects a cycle, return
false
. Otherwise, returntrue
.
Explanation
The key idea is to use topological sorting. A topological sort is a linear ordering of 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 topological sort is possible, then it's possible to complete all courses. If a cycle exists, a topological sort is not possible. Our DFS approach effectively detects cycles while implicitly attempting a topological sort.
Code (C#)
using System;
using System.Collections.Generic;
public class Solution {
public bool CanFinish(int numCourses, int[][] prerequisites) {
// Create adjacency list to represent the graph
List<int>[] graph = new List<int>[numCourses];
for (int i = 0; i < numCourses; i++) {
graph[i] = new List<int>();
}
foreach (int[] pre in prerequisites) {
graph[pre[1]].Add(pre[0]); // Add directed edge from prerequisite to course
}
// Sets to track visited and completed nodes
HashSet<int> visited = new HashSet<int>();
HashSet<int> completed = new HashSet<int>();
// DFS function to detect cycles
bool dfs(int course) {
if (visited.Contains(course)) return false; // Cycle detected
if (completed.Contains(course)) return true; // Already processed
visited.Add(course);
foreach (int neighbor in graph[course]) {
if (!dfs(neighbor)) return false; // Cycle found in neighbor
}
visited.Remove(course);
completed.Add(course);
return true;
}
// Check for cycles starting from each node
for (int i = 0; i < numCourses; i++) {
if (!dfs(i)) return false;
}
return true;
}
}
Complexity Analysis
- Time Complexity: O(V + E), where V is the number of courses (vertices) and E is the number of prerequisites (edges). This is because the DFS visits each node and edge at most once.
- Space Complexity: O(V), primarily due to the
visited
andcompleted
sets and the adjacency list representation of the graph. In the worst case, all vertices might be in these sets simultaneously.