-
Notifications
You must be signed in to change notification settings - Fork 39
/
Copy pathproject_build_order.py
102 lines (85 loc) · 2.87 KB
/
project_build_order.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
#!/usr/bin/python
# Date: 2020-10-23
#
# Description:
# Given a list of projects and a list of dependencies(which is a list of pairs
# of projects, where the second project is dependent on the first project). All
# of project's dependencies must be build before the project is. Find a build
# order that will allow the projects to build. If there is no valid build
# order, return an error.
#
# Approach:
# - Build a graph having adjacent vertices as dependents
# - Do topological sort to print correct build order
# - In case created graph has a cycle(has back edge) then build order is not
# possible
#
# Complexity:
# O(P + D), P is the number of projects and D is the number of dependency pairs
class Graph:
def __init__(self, nodes):
self.g = {}
def add_dependency(self, depends_on, dependent):
if dependent in self.g:
self.g[dependent].append(depends_on)
else:
self.g[dependent] = [depends_on]
def cycle_wrt_node(self, node, visited):
visited[node] = True
for adj in self.g[node]:
if adj in visited:
if not visited[adj]:
return self.cycle_wrt_node(adj, visited)
else:
return True
def has_cycle(self):
for node in self.g:
visited = {n: False for n in self.g}
if self.cycle_wrt_node(node, visited):
raise ValueError(
f'Project not possible, cyclic dependency for {node}')
def print_topological_order(self, node, visited):
if node in visited:
return
for dep in self.g.get(node, []):
self.print_topological_order(dep, visited)
visited.add(node)
print(node, end=' ')
def print_project_build_order(self):
self.has_cycle()
visited = set()
for node in self.g:
self.print_topological_order(node, visited)
print()
"""
x > [y, z]
y > [w]
z > [w]
"""
def main():
# Case: 1
g = Graph(['a', 'b', 'c', 'd', 'e', 'f'])
g.add_dependency('a', 'd') # d depends on a
g.add_dependency('f', 'b')
g.add_dependency('b', 'd')
g.add_dependency('f', 'a')
g.add_dependency('d', 'c')
g.add_dependency(None, 'e')
g.print_project_build_order() # f a b d c e
# Case: 2
g = Graph(['w', 'x', 'y', 'z'])
g.add_dependency('x', 'y')
g.add_dependency('x', 'z')
g.add_dependency('y', 'w')
g.add_dependency('z', 'w')
g.print_project_build_order()
# Case: 3
g = Graph(['a', 'b', 'c', 'd', 'e', 'f'])
g.add_dependency('a', 'd') # d depends on a
g.add_dependency('f', 'b')
g.add_dependency('b', 'd')
g.add_dependency('f', 'a')
g.add_dependency('d', 'c')
g.add_dependency('d', 'a') # Create cycle
g.print_project_build_order() # ValueError: Project not possible, cyclic dependency for d
main()