简体   繁体   中英

Python: Finding the longest path

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

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])

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)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM