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.
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.
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.
1 <= numCourses <= 20000 <= prerequisites.length <= 5000prerequisites[i].length == 20 <= ai, bi < numCoursesThis is a Cycle Detection problem!
A course schedule is valid if and only if the prerequisite graph has no cycles.
Can be solved with:
Use three states: unvisited (0), visiting (1), visited (2).
BFS-based approach using in-degrees.
Alternative DFS using recursion stack tracking.
Where V = numCourses, E = prerequisites.length
This problem demonstrates:
1import java.util.*;
2
3class Solution {
4 // Solution 1: DFS Cycle Detection (Three Colors)
5 public boolean canFinish(int numCourses, int[][] prerequisites) {
6 // Build adjacency list
7 List<List<Integer>> graph = new ArrayList<>();
8 for (int i = 0; i < numCourses; i++) {
9 graph.add(new ArrayList<>());
10 }
11 for (int[] prereq : prerequisites) {
12 graph.get(prereq[1]).add(prereq[0]);
13 }
14
15 // States: 0 = unvisited, 1 = visiting, 2 = visited
16 int[] state = new int[numCourses];
17
18 // Check each course
19 for (int course = 0; course < numCourses; course++) {
20 if (hasCycle(course, graph, state)) {
21 return false;
22 }
23 }
24
25 return true;
26 }
27
28 private boolean hasCycle(int course, List<List<Integer>> graph, int[] state) {
29 if (state[course] == 1) { // Currently visiting - CYCLE!
30 return true;
31 }
32 if (state[course] == 2) { // Already visited
33 return false;
34 }
35
36 // Mark as visiting
37 state[course] = 1;
38
39 // Visit all neighbors
40 for (int neighbor : graph.get(course)) {
41 if (hasCycle(neighbor, graph, state)) {
42 return true;
43 }
44 }
45
46 // Mark as visited
47 state[course] = 2;
48 return false;
49 }
50
51 // Solution 2: Topological Sort (Kahn's Algorithm)
52 public boolean canFinishBFS(int numCourses, int[][] prerequisites) {
53 // Build adjacency list and in-degree array
54 List<List<Integer>> graph = new ArrayList<>();
55 int[] inDegree = new int[numCourses];
56
57 for (int i = 0; i < numCourses; i++) {
58 graph.add(new ArrayList<>());
59 }
60
61 for (int[] prereq : prerequisites) {
62 graph.get(prereq[1]).add(prereq[0]);
63 inDegree[prereq[0]]++;
64 }
65
66 // Start with courses that have no prerequisites
67 Queue<Integer> queue = new LinkedList<>();
68 for (int course = 0; course < numCourses; course++) {
69 if (inDegree[course] == 0) {
70 queue.offer(course);
71 }
72 }
73
74 int processed = 0;
75
76 // Process courses
77 while (!queue.isEmpty()) {
78 int course = queue.poll();
79 processed++;
80
81 // Reduce in-degree of neighbors
82 for (int neighbor : graph.get(course)) {
83 inDegree[neighbor]--;
84 if (inDegree[neighbor] == 0) {
85 queue.offer(neighbor);
86 }
87 }
88 }
89
90 // All courses processed if no cycle
91 return processed == numCourses;
92 }
93
94 // Solution 3: DFS with Path Tracking
95 public boolean canFinishPath(int numCourses, int[][] prerequisites) {
96 // Build adjacency list
97 List<List<Integer>> graph = new ArrayList<>();
98 for (int i = 0; i < numCourses; i++) {
99 graph.add(new ArrayList<>());
100 }
101 for (int[] prereq : prerequisites) {
102 graph.get(prereq[1]).add(prereq[0]);
103 }
104
105 Set<Integer> visited = new HashSet<>();
106 Set<Integer> path = new HashSet<>();
107
108 // Check each course
109 for (int course = 0; course < numCourses; course++) {
110 if (!visited.contains(course)) {
111 if (!dfs(course, graph, visited, path)) {
112 return false;
113 }
114 }
115 }
116
117 return true;
118 }
119
120 private boolean dfs(int course, List<List<Integer>> graph,
121 Set<Integer> visited, Set<Integer> path) {
122 if (path.contains(course)) { // Cycle detected!
123 return false;
124 }
125 if (visited.contains(course)) { // Already checked
126 return true;
127 }
128
129 // Add to current path
130 path.add(course);
131
132 // Visit neighbors
133 for (int neighbor : graph.get(course)) {
134 if (!dfs(neighbor, graph, visited, path)) {
135 return false;
136 }
137 }
138
139 // Remove from path (backtrack)
140 path.remove(course);
141 visited.add(course);
142 return true;
143 }
144}
145