Reputation: 12452
I have a simple graph created as such in the below
class Job():
def __init__(self, name, weight):
self.name = name
self.weight = weight
self.depends = []
def add_dependent(self, dependent):
self.depends.append(dependent)
jobA = Job('A', 0)
jobB = Job('B', 4)
jobC = Job('C', 2)
jobD = Job('D', 10)
jobE = Job('E', 3)
jobF = Job('F', 11)
jobA.add_dependent(jobB)
jobA.add_dependent(jobC)
jobB.add_dependent(jobD)
jobC.add_dependent(jobE)
jobD.add_dependent(jobF)
jobE.add_dependent(jobF)
so we have two possible paths
A->B->D->F 0+4+10+11 = 25
A->C->E->F 0+2+3+11 = 16
so the longest paths would be the former
Is there an easy way to gather the longest path, A->B->D->F
?
def longest_path(root):
paths = []
# some logic here
return paths
print longest_path(jobA) # should print A->B->D->F
Upvotes: 1
Views: 2111
Reputation: 5031
Not the most efficient solution, but here is one that should work:
import operator
def longest_path(root):
def _find_longest(job):
costs = [_find_longest(depend) for depend in job.depends]
if costs:
# Find most expensive:
path, cost = max(costs, key=operator.itemgetter(1))
return ([job.name] + path, job.weight + cost)
else:
return ([job.name], job.weight)
return "->".join(_find_longest(root)[0])
Upvotes: 2
Reputation: 3582
If you use OO solution, it's easy to provide a way to store only the heaviest path. This is the solution I came up with - using a callable class
In [111]: class Heaviest(object):
...: def __init__(self, job):
...: self.path = ''
...: self.weight = 0
...: self.job = job
...: def _find_heaviest(self, job, path='', weight=0):
...: path += job.name
...: weight += job.weight
...: if not job.depends:
...: if weight > self.weight:
...: self.weight = weight
...: self.path = path
...: else:
...: for job in job.depends:
...: self._find_heaviest(job, path, weight)
...: def __call__(self):
...: self._find_heaviest(self.job)
...: return '->'.join(list(self.path)), self.weight
...:
In [112]: Heaviest(jobA)()
Out[112]: ('A->B->D->F', 25)
An afterthought:
It occurred to me last night that in case of cyclic dependency (see my comment), the solution above will not yield an answer, stopping with exception when maximum recursion depth is reached. Just adding the line below will blow any tree traversing algorithm - not just this one.
In [226]: jobF.add_dependent(jobA)
In [227]: Heaviest(jobA)()
---------------------------------------------------------------------------
RuntimeError Traceback (most recent call last)
<ipython-input-227-94e994624b4e> in <module>()
----> 1 Heaviest(jobA)()
<ipython-input-111-1ff9f69480a9> in __call__(self)
15 self._find_heaviest(job, path, weight)
16 def __call__(self):
---> 17 self._find_heaviest(self.job)
18 return '->'.join(list(self.path)), self.weight
19
<ipython-input-111-1ff9f69480a9> in _find_heaviest(self, job, path, weight)
13 else:
14 for job in job.depends:
---> 15 self._find_heaviest(job, path, weight)
16 def __call__(self):
17 self._find_heaviest(self.job)
... last 1 frames repeated, from the frame below ...
<ipython-input-111-1ff9f69480a9> in _find_heaviest(self, job, path, weight)
13 else:
14 for job in job.depends:
---> 15 self._find_heaviest(job, path, weight)
16 def __call__(self):
17 self._find_heaviest(self.job)
RuntimeError: maximum recursion depth exceeded
While I leave attempt to mend the implementation to you - if you wish - simple safeguard can fix that
def _find_heaviest(self, job, path='', weight=0):
if not job.name in path:
path += job.name
weight += job.weight
stop_search = not job.depends
else:
stop_search = True
if stop_search:
if weight > self.weight:
.....
Problem solved
In [230]: Heaviest(jobA)()
Out[230]: ('A->B->D->F', 25)
Upvotes: 1