简体   繁体   中英

Finding all the shortest paths between two nodes in unweighted undirected graph

I need help finding all the shortest paths between two nodes in an unweighted undirected graph .

I am able to find one of the shortest paths using BFS, but so far I am lost as to how I could find and print out all of them.

Any idea of the algorithm / pseudocode I could use?

As a caveat, remember that there can be exponentially many shortest paths between two nodes in a graph. Any algorithm for this will potentially take exponential time.

That said, there are a few relatively straightforward algorithms that can find all the paths. Here's two.

BFS + Reverse DFS

When running a breadth-first search over a graph, you can tag each node with its distance from the start node. The start node is at distance 0, and then, whenever a new node is discovered for the first time, its distance is one plus the distance of the node that discovered it. So begin by running a BFS over the graph, writing down the distances to each node.

Once you have this, you can find a shortest path from the source to the destination as follows. Start at the destination, which will be at some distance d from the start node. Now, look at all nodes with edges entering the destination node. A shortest path from the source to the destination must end by following an edge from a node at distance d-1 to the destination at distance d. So, starting at the destination node, walk backwards across some edge to any node you'd like at distance d-1. From there, walk to a node at distance d-2, a node at distance d-3, etc. until you're back at the start node at distance 0.

This procedure will give you one path back in reverse order, and you can flip it at the end to get the overall path.

You can then find all the paths from the source to the destination by running a depth-first search from the end node back to the start node, at each point trying all possible ways to walk backwards from the current node to a previous node whose distance is exactly one less than the current node's distance.

(I personally think this is the easiest and cleanest way to find all possible paths, but that's just my opinion.)

BFS With Multiple Parents

This next algorithm is a modification to BFS that you can use as a preprocessing step to speed up generation of all possible paths. Remember that as BFS runs, it proceeds outwards in "layers," getting a single shortest path to all nodes at distance 0, then distance 1, then distance 2, etc. The motivating idea behind BFS is that any node at distance k + 1 from the start node must be connected by an edge to some node at distance k from the start node. BFS discovers this node at distance k + 1 by finding some path of length k to a node at distance k, then extending it by some edge.

If your goal is to find all shortest paths, then you can modify BFS by extending every path to a node at distance k to all the nodes at distance k + 1 that they connect to, rather than picking a single edge. To do this, modify BFS in the following way: whenever you process an edge by adding its endpoint in the processing queue, don't immediately mark that node as being done. Instead, insert that node into the queue annotated with which edge you followed to get to it. This will potentially let you insert the same node into the queue multiple times if there are multiple nodes that link to it. When you remove a node from the queue, then you mark it as being done and never insert it into the queue again. Similarly, rather than storing a single parent pointer, you'll store multiple parent pointers, one for each node that linked into that node.

If you do this modified BFS, you will end up with a DAG where every node will either be the start node and have no outgoing edges, or will be at distance k + 1 from the start node and will have a pointer to each node of distance k that it is connected to. From there, you can reconstruct all shortest paths from some node to the start node by listing of all possible paths from your node of choice back to the start node within the DAG. This can be done recursively:

  • There is only one path from the start node to itself, namely the empty path.
  • For any other node, the paths can be found by following each outgoing edge, then recursively extending those paths to yield a path back to the start node.

This approach takes more time and space than the one listed above because many of the paths found this way will not be moving in the direction of the destination node. However, it only requires a modification to BFS, rather than a BFS followed by a reverse search.

Hope this helps!

@templatetypedef is correct, but he forgot to mention about distance check that must be done before any parent links are added to node. This means that se keep the distance from source in each of nodes and increment by one the distance for children. We must skip this increment and parent addition in case the child was already visited and has the lower distance.

public void addParent(Node n) {
    // forbidding the parent it its level is equal to ours
    if (n.level == level) {
        return;
    }
    parents.add(n);

    level = n.level + 1;
}

The full java implementation can be found by the following link.

http://ideone.com/UluCBb

I encountered the similar problem while solving this https://oj.leetcode.com/problems/word-ladder-ii/

The way I tried to deal with is first find the shortest distance using BFS, lets say the shortest distance is d. Now apply DFS and in DFS recursive call don't go beyond recursive level d.

However this might end up exploring all paths as mentioned by @templatetypedef.

First, find the distance-to-start of all nodes using breadth-first search.

(if there are a lot of nodes, you can use A* and stop when top of the queue has distance-to-start > distance-to-start(end-node) . This will give you all nodes that belong to some shortest path)

Then just backtrack from the end-node. Anytime a node is connected to two (or more) nodes with a lower distance-to-start, you branch off into two (or more) paths.

templatetypedef your answer was very good, thank you a lot for that one(!!), but it missed out one point:

If you have a graph like this:

A-B-C-E-F
  |     |
  D------

Now lets imagine I want this path:

A -> E.

It will expand like this:

A-> B -> D-> C -> F -> E.

The problem there is, that you will have F as a parent of E, but

A->B->D->F-E
is longer than

A->B->C->E. 
You will have to take of tracking the distances of parents you are so happily adding.

Step 1: Traverse the graph from the source by BFS and assign each node the minimal distance from the source

Step 2: The distance assigned to the target node is the shortest length

Step 3: From source, do a DFS search along all paths where the minimal distance is increased one by one until the target node is reached or the shortest length is reached. Print the path whenever the target node is reached.

BFS stops when you find what you want.

You have to modify the algorithm so it continues its execution when the first path is found. (delete the return statement and save the path somehow.

You may end the execution after check the last node of the level that has the ending nodes of the shortest paths. (All ending nodes of the shortest paths are at the same level)


Also, there known algorithm that find all shortest paths:

Floyd–Warshall algorithm (it has pseudocode)

Johnson's algorithm

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