Adjacency list makes runtime go down from O(V×E) to O(V+E) for the adjacency list creation
TLE would give O((V+E)×V), where V is the number of nodes in the graph and E is the connections.
That is because you must run DFS for each node on all the nodes it is connected to (which could be all of them).
To speed up, we use a DP dictionary to store the previous computation:
If a node reaches another node which had previously been computed fully, then the node that reached it must also contain the same result.
Alternatively to save space, we could just clear that dependency list such that the algorithm immediately returns true due to having no prerequisites and immediately returning true.
Topological Sort takes O(V+E) to generate the adjacency list as well as
Space
O(V+E) to hold the dependency list, which contains all the nodes and edges connected directly to those nodes.
An additional O(V) space if using the DP dictionary.
Code
Topological Sort
class Solution: def canFinish(self, numCourses: int, prerequisites: List[List[int]]) -> bool: indegree = [0] * numCourses dependency = defaultdict(list) for u, v in prerequisites: dependency[u].append(v) indegree[v] += 1 q = deque() for i, degree in enumerate(indegree): if degree == 0: q.append(i) while q: u = q.popleft() if u in dependency: for v in dependency[u]: indegree[v] -= 1 if indegree[v] == 0: q.append(v) return sum(indegree) == 0
Dependency Clearing
class Solution: def canFinish(self, numCourses: int, prerequisites: List[List[int]]) -> bool: dependency = defaultdict(list) for u, v in prerequisites: dependency[u].append(v) seen = set() valid = {} def dfs(u): if u in seen: return False if u not in dependency: return True seen.add(u) for v in dependency[u]: if dfs(v) == False: valid[u] = False return False seen.remove(u) dependency[u] = [] return True for u in dependency.keys(): if dfs(u) == False: return False return True
DP Dictionary
class Solution: def canFinish(self, numCourses: int, prerequisites: List[List[int]]) -> bool: dependency = defaultdict(list) for u, v in prerequisites: dependency[u].append(v) seen = set() valid = {} def dfs(u): if u in seen: valid[u] = False if u not in dependency: valid[u] = True if u in valid: return valid[u] seen.add(u) for v in dependency[u]: if dfs(v) == False: valid[u] = False return False seen.remove(u) valid[u] = True return True for u in dependency.keys(): if dfs(u) == False: return False return True
TLE
class Solution: def canFinish(self, numCourses: int, prerequisites: List[List[int]]) -> bool: dependency = defaultdict(list) for u, v in prerequisites: dependency[u].append(v) seen = set() def dfs(u): if u in seen: return False if u not in dependency: return True seen.add(u) for v in dependency[u]: if dfs(v) == False: return False seen.remove(u) return True for u in dependency.keys(): if dfs(u) == False: return False return True