"""
This implementation demonstrates how to
perform topological sort, which yields
a linear ordering of the vertices of a
graph. Example considered is that of
inter-dependent jobs.
Input: List of jobs and list of dependencies
Output: A valid order in which jobs can be
completed
Let j be the number of jobs and d the number
of dependencies.
Time complexity: O(j+d)
Space complexity: O(j+d)
"""
def topological_sort(jobs, deps):
job_graph = create_graph(jobs, deps)
return get_ordered_jobs(job_graph)
def create_graph(jobs, deps):
graph = job_graph(jobs)
for prereq, job in deps:
graph.add_prereq(job, prereq)
return graph
def get_ordered_jobs(graph):
ordered_jobs = []
nodes = graph.nodes
while len(nodes):
node = nodes.pop()
print(node.job)
contains_cycle = depth_first_search(node, ordered_jobs)
if contains_cycle:
return []
return ordered_jobs
def depth_first_search(node, ordered_jobs):
print(node.job)
if node.visited:
return False
if node.visiting:
return True
node.visiting = True
for prereq_node in node.prereqs:
contains_cycle = depth_first_search(prereq_node, ordered_jobs)
if contains_cycle:
return True
node.visited = True
node.visiting = False
ordered_jobs.append(node.job)
print(ordered_jobs)
return False
class job_graph:
def __init__(self, jobs):
self.nodes = []
self.graph = {}
for job in jobs:
self.add_node(job)
def add_prereq(self, job, prereq):
job_node = self.get_node(job)
prereq_node = self.get_node(prereq)
job_node.prereqs.append(prereq_node)
def add_node(self, job):
self.graph[job] = job_node(job)
self.nodes.append(self.graph[job])
def get_node(self, job):
if job not in self.graph:
self.add_node(job)
return self.graph[job]
class job_node:
def __init__(self, job):
self.job = job
self.prereqs = []
self.visited = False
self.visiting = False
jobs = [1, 2, 3, 4]
deps = [[1, 2], [1, 3], [3, 2], [4, 2], [4, 3]]
print(topological_sort(jobs, deps))