pythongraph-algorithmpath-finding

Finding shortest path in a cyclic weighted graph


I am trying to find shortest path in a cyclic weighted graph. I have the following recursive function that is able to find shortest path between 2 points if they are not cyclic. For instance my graph looks like this:

graph = {
          'A': {'B': 5, 'D': 5, 'E': 7 },
          'B': {'C': 4},
          'C': {'D': 8, 'E': 2},
          'D': {'C': 8, 'E':  6},
          'E': {'B': 3}
        }

And now my find shortestRoute function looks like this

def findShortestRoute(self, start, end , weight, shortestRoute):
    # sys.setrecursionlimit(10000)
    visited = []
    # Check if start and end nodes exists in route table
    if start in self.routesTable and end in self.routesTable:
        visited.append(start) # mark start to visited

        for adj in self.routesTable[start]:
            if(adj == end or adj not in visited):
                weight += self.routesTable[start][adj]

            '''
            If destination matches, we compare
            weight of this route to shortest route
            so far, and make appropriate switch
            '''

            if adj == end:
                if shortestRoute == 0 or weight < shortestRoute:
                    shortestRoute = weight
                visited.remove(start)
                return shortestRoute
            '''
            If destination does not match, and
            destination node has not yet been visited,
            we recursively traverse destination node
            '''
            if adj not in visited:
                shortestRoute = self.findShortestRoute(adj, end, weight, shortestRoute)
                weight -= self.routesTable[start][adj]

    else:
        return "No such route exists"

    if start in visited:
        visited.remove(start)

    return shortestRoute

Now for non-cyclic cases if I do something like

findShortestRoute('A', 'C', 0, 0)

this will return 9 which is what expected. However, if I do something like this findShortestRoute('B', 'B', 0, 0)

The function will reach stack overflow. however, since the graph is cyclic there is a way to start from B and get back to B. In this case B-C-E-B which weights 9. But my function is reaching maximum recursion. I would really appreciate if someone can help me with this. Thanks in advance


Solution

  • Your problem is that you do not pass the visited list through your recursion, so your algorithm gets stuck in cycles. If you pass down the predecessors nodes you can detect cycles and return if you hit one.

    Below is how I would change your code to achieve this:

    def findShortestRoute(start, end , weight, shortestRoute, visited, routesTable):
    # sys.setrecursionlimit(10000)
    # Check if start and end nodes exists in route table
    
    if start in routesTable and end in routesTable:
        if start in visited:
            return 99999
        visited.append(start) # mark start to visited
    
        for adj in routesTable[start]:
            if(adj == end or adj not in visited):
                weight += routesTable[start][adj]
    
            '''
            If destination matches, we compare
            weight of this route to shortest route
            so far, and make appropriate switch
            '''
    
            if adj == end:
                if shortestRoute == 0 or weight < shortestRoute:
                    shortestRoute = weight
                visited.remove(start)
                return shortestRoute
            '''
            If destination does not match, and
            destination node has not yet been visited,
            we recursively traverse destination node
            '''
            if adj not in visited:
                shortestRoute = findShortestRoute(adj, end, weight, shortestRoute, visited, routesTable)
                weight -= routesTable[start][adj]
    else:
        return "No such route exists"
    
    
    return shortestRoute
    
    graph = {'A': {'B': 5, 'D': 5, 'E': 7 },'B': {'C': 4},'C': {'D': 8, 'E': 2},'D': {'C': 8, 'E':  6},'E': {'B': 3}}
    
    print(findShortestRoute('B', 'B', 0, 0, [], graph))