Given numCourses courses labeled from 0 to numCourses - 1 and a list of prerequisite pairs, return whether all courses can be finished.
Each pair [course, prerequisite] means the prerequisite course must be completed before the course. Completion is possible only when the prerequisite graph has no directed cycle.
Example 1
Input: numCourses = 2, prerequisites = [[1,0]]
Output: true
Explanation: Course 0 can be completed before course 1.
Example 2
Input: numCourses = 2, prerequisites = [[1,0],[0,1]]
Output: false
Explanation: Course 0 and course 1 depend on each other, forming a cycle.
Approach 1
Each course becomes a vertex, and every pair creates a directed edge from prerequisite to dependent course. Depth First Search can detect whether a dependency chain returns to a course already active in the current DFS path.
A visited array tracks courses already explored globally, while a path-visited array tracks courses in the active recursion chain. Reaching a path-visited course means circular dependency, so completing every course becomes impossible.
Algorithm
Build an adjacency list with a directed edge from each prerequisite course to its dependent course.
Initialize
visitedandpathVisitedarrays to track globally explored courses and courses in the current DFS path.Traverse every course to cover all disconnected components.
Start DFS from each course that has not been visited.
Mark the current course as
visitedandpathVisited.Run DFS for every unvisited dependent course and return
falseif any dependent course is alreadypathVisited.Remove the current course from
pathVisitedafter all its dependencies are processed.Return
trueif no circular dependency is found.
Dry Run
course schedule
Solution
#include <bits/stdc++.h>using namespace std;class Solution {private: // Detects a cycle through the active DFS path. bool hasCycle(int course, vector<int>& visited, vector<int>& pathVisited, vector<vector<int>>& adj) { // Mark the course as visited. visited[course] = 1; // Mark the course in the current DFS path. pathVisited[course] = 1; // Explore all courses depending on the current course. for (int nextCourse : adj[course]) { // Visit the next course if it is not visited. if (!visited[nextCourse]) { // If a cycle is found deeper, return true. if (hasCycle(nextCourse, visited, pathVisited, adj)) { return true; } } // If next course is in the current path, a cycle exists. else if (pathVisited[nextCourse]) { return true; } } // Remove course from the active DFS path. pathVisited[course] = 0; // No cycle found from this course. return false; }public: // Checks if all courses can be finished. bool canFinish(int numCourses, vector<vector<int>>& prerequisites) { vector<vector<int>> adj(numCourses); // Build edge from prerequisite to dependent course. for (auto& pairValue : prerequisites) { int course = pairValue[0]; int prerequisite = pairValue[1]; adj[prerequisite].push_back(course); } vector<int> visited(numCourses, 0); vector<int> pathVisited(numCourses, 0); // Start DFS from every unvisited course. for (int course = 0; course < numCourses; course++) { // Only unvisited courses can start a new DFS. if (!visited[course]) { // If a cycle exists, all courses cannot be finished. if (hasCycle(course, visited, pathVisited, adj)) { return false; } } } // No circular dependency exists. return true; }};// Driver code.int main() { int numCourses = 2; vector<vector<int>> prerequisites = { {1, 0} }; Solution sol; cout << (sol.canFinish(numCourses, prerequisites) ? "true" : "false"); return 0;}Complexity Analysis
Time Complexity: O(V+E), where V is the number of courses and E is the number of prerequisite relations; DFS processes every course and directed edge once.
Space Complexity: O(V+E), where the adjacency list stores V courses and E edges, while the visited arrays and recursion stack require O(V) space.
Approach 2
Kahn Algorithm treats courses with indegree 0 as immediately available because no prerequisites block them. Completing such a course removes outgoing dependency effect from dependent courses.
If all courses can be processed through repeated zero-indegree removal, no cycle exists. If some courses never reach indegree 0, a circular dependency remains.
Algorithm
Build an adjacency list and an
indegreearray from all prerequisite pairs.Add every course with
indegree = 0to a queue.Initialize
completedas0.Pop a course from the queue and increase
completed.Reduce the indegree of every dependent course.
Add a dependent course to the queue when its indegree becomes
0.Continue until the queue becomes empty.
Return
trueifcompleted == numCourses; otherwise, returnfalse.
Dry Run
Diagram 1
Solution
#include <bits/stdc++.h>using namespace std;class Solution {public: // Checks if all courses can be finished using Kahn's Algorithm. bool canFinish(int numCourses, vector<vector<int>>& prerequisites) { vector<vector<int>> adj(numCourses); vector<int> indegree(numCourses, 0); // Build graph and indegree from prerequisites. for (auto& pairValue : prerequisites) { int course = pairValue[0]; int prerequisite = pairValue[1]; adj[prerequisite].push_back(course); indegree[course]++; } queue<int> q; // Add courses with no prerequisites. for (int course = 0; course < numCourses; course++) { // Zero indegree means this course can be completed now. if (indegree[course] == 0) { q.push(course); } } int completed = 0; // Complete courses in prerequisite-safe order. while (!q.empty()) { int course = q.front(); q.pop(); // Count the current course as completed. completed++; // Remove current course as prerequisite for dependent courses. for (int nextCourse : adj[course]) { indegree[nextCourse]--; // If all prerequisites are done, add this course. if (indegree[nextCourse] == 0) { q.push(nextCourse); } } } // All courses are possible only when all get completed. return completed == numCourses; }};// Driver code.int main() { int numCourses = 2; vector<vector<int>> prerequisites = { {1, 0}, {0, 1} }; Solution sol; cout << (sol.canFinish(numCourses, prerequisites) ? "true" : "false"); return 0;}Complexity Analysis
Time Complexity: O(V+E), where V is the number of courses and E is the number of prerequisite relations; every course and directed edge is processed once.
Space Complexity: O(V+E), where the adjacency list stores V courses and E edges, while the indegree array and queue require O(V) space.
Interview follow-up Questions
Courses are vertices, and prerequisite pairs are directed edges from prerequisite to dependent course.
Be the first to add a comment.