2025-09-18 14:05
Status: child
Tags: leetcode leetcode-medium graphs google Leetcode Graphs Intro computer-science
Course Schedule
Code
from collections import defaultdict
class Solution(object):
def canFinish(self, numCourses, prerequisites):
"""
:type numCourses: int
:type prerequisites: List[List[int]]
:rtype: bool
"""
# iterative dfs with a stack
g = defaultdict(list)
courses = prerequisites
for a, b in courses:
g[a].append(b)
UNVISITED = 0
VISITING = 1
VISITED = 2
states = [UNVISITED] * numCourses
def dfs(node):
state = states[node]
if state == VISITED: return True
elif state == VISITING: return False
states[node] = VISITING
for nei in g[node]:
if not dfs(nei):
return False
states[node] = VISITED
return True
for i in range(numCourses):
if not dfs(i):
return False
return True
Explanation
- The tricky part about this problem is that there could be cycles.
- To detect cycles, we assign each node one of three possible states
- UNVISITED (0) → we haven’t explored this node yet
- VISITING (1) → this node is in the current DFS path (our stack/recursion chain)
- VISITED (2) → this node and all of its neighbors have already been fully processed
- The key intuition: if during DFS we ever hit a node that is already in the VISITING state (1), we’ve circled back into our path → meaning there is a cycle → return False immediately.
- Otherwise, once we finish exploring all neighbors of a node, we can safely mark it as VISITED (2). That way, if we see it again later, we know it has no cycles downstream.
- We initialize all nodes as UNVISITED in a separate list
states
. DFS updates this list as it goes. - The algorithm simply runs DFS for every course (node). If any of them ever leads to a cycle, we return False. If we finish all without issues, then it’s possible to take all courses → return True.