207. Course Schedule - cocoder39/coco39_LC GitHub Wiki
Option 1 - DFS approach: this is an approach used to detect cycle in directed graph (see reference)
- visited is used to avoid redundant checks: eg b->a, c->b, then we can avoid re-checking b
- path is used to detect loop:
- to maintain a recursion stack we need to use back track
- if there is cycle in a recursion stack, then there is loop
class Solution:
def canFinish(self, numCourses: int, prerequisites: List[List[int]]) -> bool:
pre_to_next = collections.defaultdict(list)
for next_course, pre_course in prerequisites:
pre_to_next[pre_course].append(next_course)
visited = [False] * numCourses
in_stack = [False] * numCourses
for course in range(numCourses):
if not visited[course]:
# in_stack = [False] * numCourses, same effect putting it here or up as there is backtrack. up is more efficient
if self.hasCycle(course, pre_to_next, in_stack, visited):
return False
return True
def hasCycle(self, cur: int, pre_to_next: Dict[int, int], in_stack: List[bool], visited: List[bool]) -> bool:
visited[cur] = True
in_stack[cur] = True
for next_course in pre_to_next[cur]:
if in_stack[next_course]: # in currrent recurision stack
return True
elif not visited[next_course] and self.hasCycle(next_course, pre_to_next, in_stack, visited):
return True
in_stack[cur] = False
return False
Option 2: topological sort
class Solution:
def canFinish(self, numCourses: int, prerequisites: List[List[int]]) -> bool:
adjacent_list = collections.defaultdict(list)
in_degrees = collections.defaultdict(int)
for prerequisite in prerequisites:
adjacent_list[prerequisite[1]].append(prerequisite[0])
in_degrees[prerequisite[0]] += 1
in_degrees[prerequisite[1]] += 0
queue = collections.deque()
for key in in_degrees:
if in_degrees[key] == 0:
queue.append(key)
count = 0
while queue:
i = queue.popleft()
count += 1
for j in adjacent_list[i]:
in_degrees[j] -= 1
if in_degrees[j] == 0:
queue.append(j)
return len(in_degrees) == count
Complexity: E is #edges and V is #vertexes in graph.
- Step 1: building graph O(E) time complexity. O(E+V) space complexity for adjacent_list and O(V) space complexity for in_degrees
- Step 2: iterate in-degree dict O(V)
- Step 3: BFS visits each vertex and each edge once O(E + V)
Total time complexity is O(E + V), space complexity is O(E+V)