There are a total of n courses you have to take, labeled from 0 to n - 1.
Some courses may have prerequisites, for example to take course 0 you have to first take course 1, which is expressed as a pair: [0,1]
Given the total number of courses and a list of prerequisite pairs, is it possible for you to finish all courses?
For example:
2, [[1,0]]
There are a total of 2 courses to take. To take course 1 you should have finished course 0. So it is possible.
2, [[1,0],[0,1]]
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.
Note:
The input prerequisites is a graph represented by a list of edges, not adjacency matrices. Read more about how a graph is represented.
You may assume that there are no duplicate edges in the input prerequisites.
The Idea: Implement topological sort. First construct an adjacency set graph. Iterate through the graph to find a independent node. This node represents one possible course to take. Decrement numCourses as independent courses are selected. Once a course is selected, removed this course from the graph, and all other dependancies it has. Repeat this process until numCourses is 0 (all fullfilled), or a cycle is found (this is identified when no independent courses are found). Finally, return True if the graph is empty (all courses are fullfilled), and False otherwise.
Complexity: O(n^3) time, and O(n) space
defcanFinish(self,numCourses,prerequisites):""" :type numCourses: int :type prerequisites: List[List[int]] :rtype: bool """ifnot prerequisites ornot prerequisites[0]:returnTrue g ={}for pair in prerequisites:ifnot g.__contains__(pair[0]): g[pair[0]]=set()ifnot g.__contains__(pair[1]): g[pair[1]]=set()for pair in prerequisites: g[pair[0]].add(pair[1]) stop =Falsewhilenot stop: stop =Trueif numCourses is0:returnTruefor key, _set in g.items():ifnot _set: stop =False numCourses -=1del g[key]for _, _set2 in g.items():if key in _set2: _set2.remove(key)breakifnot g:returnTrueelse:returnFalse
O(N) time solution
The first thing to recognize is that if a graph is acyclic, then it must have a topological ordering. In the same way, if the graph has a topological ordering, then it is acyclic. So we can boil this problem down to answering the question whether or not the graph contains any cycles. If there are cycles, then the courses can not be completed. Otherwise this must exist a topological ordering, and the so the courses can be finished.
A cycle exists in a graph if at any time during we reach a node that is already within our recusion stack. When this is the case, we know that their exists a path from where we started (bottom of stack) to somewhere we've already been (somewhere in recursion stack). To avoid repeating the same recursions we maintain a visited set that always collects unvisited nodes we come across.
classSolution:defget_directed_graph(self,prereqs): g ={}for pair in prereqs: g[pair[0]]=set() g[pair[1]]=set()for pair in prereqs: g[pair[0]].add(pair[1])return gdefcyclic(self,g): path =set() visited =set()defDFS(vertex):if vertex in visited:returnFalse visited.add(vertex) path.add(vertex)for neighbour in g[vertex]:if neighbour in path orDFS(neighbour):returnTrue path.remove(vertex)returnFalsereturnany(DFS(v) for v in g)defcanFinish(self,numCourses,prerequisites):""" :type numCourses: int :type prerequisites: List[List[int]] :rtype: bool """# no prereqs -> [[]]ifnot prerequisites ornot prerequisites[0]:returnTrue# create directed graph g = self.get_directed_graph(prerequisites)if self.cyclic(g):returnFalsereturnTrue