* Two-source breadth-first search
- many times we need the distance between only two given vertices s and t
- one way is to calculate distances from s to all other vertices, and then
read the distance to t
- even better, we could run the breadth-first search only till the vertex
t is found
- the best strategy however, is to run two breadth-first searches starting
from the two vertices s and t
- the point at which one BFS assigns distance to the vertex which has
already been assigned a distance by the other BFS, we stop both BFSs!
- the sum of the distances assigned by the two BFSs to this common vertex
is the shortest distance between the two vertices
- running time in the single source case: around n/2 vertices are located
till vertex t is found. For each of them, neighbors are located in O(m/n)
time. Thus, in total, this takes O(m), same as the BFS
- in the two source case, each BFS stops after finding vertices up to
distance d/2. Since the number of vertices increases exponentially with d,
c^d = n/2. Hence, c^(d/2) ~ sqrt(n/2). For the two BFSs, this takes
O(sqrt(n)). Hence the total running time goes as O(m/sqrt(n)).
* Finding shortest paths
- BFS doesn't directly give us shortest paths, it only tells us the
shortest distance between the vertices
- A small modification in BFS gives us a shortest path between given two
vertices
- each time an unseen vertex j is found, apart from assigning it the
distance, we also create a pointer from it to its predecessor i. This
pointer could just be an integer associated with j, and all pointers can
be saved using an array of size n
- Once the BFS finishes, the pointers form a shortest-path tree for the
source vertex
- Using this tree, we can create a shortest path from source to any vertex
- Pointers can be created in time O(1) for each vertex, and hence in time
O(n) in total. Thus, the total running time is still O(m + n) like the
standard BFS
- this algorithm however finds only 1 path, whereas multiple shortest
paths can exist between a given pair of vertices
- Finding all paths can be done by saving all predecessors for each vertex
i.e. a vertex that is a neighbor of j, and has distance d from the source
- if the neighbor j has already been assigned distance d+1, we know that
it has another shortest path via the current vertex i. Hence, we save i
as a predecessor of j
- at the end of the BFS, we again have a shortest-path "tree" (which now is
not a tree, but a DAG). All shortest paths from the source can be created
using this tree.
- using two-source BFS, this can be done in time O(m/sqrt(n)). However,
now we can't stop after we find a vertex common to both BFS because there
could be multiple shortest paths, and to make sure that we don't miss any
of those, we locate all the vertices which are common to both BFS. By
construction, they are at the same distance from s and t
- computing all shortest paths between s and t could be tricky, since all
possible combinations of pointers should be used.