Topological sorting

Jimmy (xiaoke) Shen
3 min readApr 15, 2020

--

210. Course Schedule II

The following code is trying to detect the cycle by counting the number of iterations. If the number of iterations is larger than n, it means we have a cycle, return [], else return the result

DFS approach (using the number of iterations of detect cycle)

class Solution:
def findOrder(self, n: int, prerequisites: List[List[int]]) -> List[int]:

if not prerequisites:return list(range(n))
graph = collections.defaultdict(list)
for a,b in prerequisites:
graph[b].append(a)
visited = [False]*n
res = []
self.iteration=0
def dfs(i):
self.iteration +=1
if self.iteration>n:return False
for j in graph[i]:
if not visited[j]:
if not dfs(j):return False
visited[i] = True
res.append(i)
return True
for i in range(n):
if not visited[i]:
if not dfs(i):return []
return res[::-1]

DFS approach (standard Tarjan’s algorithm)

UNVISITED = 1
EXPLORED = 2
VISITED = 3
class Solution:

def findOrder(self, numCourses: int, prerequisites: List[List[int]]) -> List[int]:
# Create the adjacency list representation of the graph
graph = defaultdict(list)
# A pair [a, b] in the input represents edge from b --> a
for dest,src in prerequisites:
graph[src].append(dest)
res = []
self.hascycle = False
# By default all vertces are UNVISITED
vertex_state = [UNVISITED]*numCourses
def dfs(node):
# Don't recurse further if we found a cycle already
if self.hascycle:return
# Start the recursion
vertex_state[node] = EXPLORED
# Traverse on neighboring vertices
if node in graph:
for neighbor in graph[node]:
if vertex_state[neighbor] == UNVISITED:
dfs(neighbor)
elif vertex_state[neighbor] == EXPLORED:
# An edge to a GRAY vertex represents a cycle
self.hascycle=True
return
# Recursion ends. We mark it as black
vertex_state[node] = VISITED
res.append(node)
for i in range(numCourses):
# If the node is unprocessed, then call dfs on it.
if vertex_state[i] == UNVISITED:
dfs(i)
return [] if self.hascycle else res[::-1]

time complexity O(V+E)

space complexity O(V+E)

BFS approach (Kahn’s algorithm)

time complexity O(V+E)

“A DAG G has at least one vertex with in-degree 0 and one vertex with out-degree 0.
Proof: There’s a simple proof to the above fact is that a DAG does not contain a cycle which means that all paths will be of finite length. Now let S be the longest path from u(source) to v(destination). Since S is the longest path there can be no incoming edge to u and no outgoing edge from v, if this situation had occurred then S would not have been the longest path
=> indegree(u) = 0 and outdegree(v) = 0

Algorithm:
Steps involved in finding the topological ordering of a DAG:

Step-1: Compute in-degree (number of incoming edges) for each of the vertex present in the DAG and initialize the count of visited nodes as 0.

Step-2: Pick all the vertices with in-degree as 0 and add them into a queue (Enqueue operation)

Step-3: Remove a vertex from the queue (Dequeue operation) and then.

  1. Increment count of visited nodes by 1.
  2. Decrease in-degree by 1 for all its neighboring nodes.
  3. If in-degree of a neighboring nodes is reduced to zero, then add it to the queue.

Step 5: Repeat Step 3 until the queue is empty.

Step 5: If count of visited nodes is not equal to the number of nodes in the graph then the topological sort is not possible for the given graph.

How to find in-degree of each node?
There are 2 ways to calculate in-degree of every vertex:
Take an in-degree array which will keep track of
1) Traverse the array of edges and simply increase the counter of the destination node by 1.

for each node in Nodes
indegree[node] = 0;
for each edge(src,dest) in Edges
indegree[dest]++

Time Complexity: O(V+E)

2) Traverse the list for every node and then increment the in-degree of all the nodes connected to it by 1.

for each node in Nodes
If (list[node].size()!=0) then
for each dest in list
indegree[dest]++;

Time Complexity: The outer for loop will be executed V number of times and the inner for loop will be executed E number of times, Thus overall time complexity is O(V+E).

The overall time complexity of the algorithm is O(V+E)”[1]

class Solution:

def findOrder(self, n: int, prerequisites: List[List[int]]) -> List[int]:
graph = collections.defaultdict(list)
indegrees = [0]*n
for dest, src in prerequisites:
graph[src].append(dest)
indegrees[dest]+=1
zero_indegree_q= collections.deque([i for i in range(n) if indegrees[i]==0])
res = []
while zero_indegree_q:
i = zero_indegree_q.popleft()
res.append(i)
for j in graph[i]:
indegrees[j]-=1
if indegrees[j]==0:
zero_indegree_q.append(j)
return res if len(res)==n else []

Reference

[1] https://www.geeksforgeeks.org/topological-sorting-indegree-based-solution/

[2] https://en.wikipedia.org/wiki/Topological_sorting

--

--

No responses yet