- James S. Plank
- November 2, 2009. Latest Modification: Tue Oct 28 09:01:14 EDT 2014
- Directory:
**/home/plank/cs302/Notes/BFS**

- The Topcoder Tutorial on Graphs, Part 2, which ends with Breadth-First Search.
- The Topcoder Tutorial on Graphs, Part 3, which covers Dijkstra's shortest path algorithm.
- Breadth-First Search
- Dijkstra's Algorithm

- BFS: The Topcoder "CarrotJumping" problem (SRM 478, D1, 250). I go over this one in class.
- BFS: The Topcoder "EmoticonsDiv1" problem (SRM 612, D1, 250. This link has an explanation of the BFS and commented code. This problem is similar in flavor to "CarrotJumping," because you build the graph as you go.
- BFS: The Topcoder "StepsConstruct" problem (SRM 707, D2, 500). I give you hints and programming tips. There is a commented solution at the end.
- BFS: The Topcoder "OneRegister" problem (SRM 486, D1, 250). I give you hints here, and not code.
- BFS: The Topcoder "CsCourses" problem (SRM 340, D1, 500). I do include a link to my code, but this is more useful if you write the code yourself.
- BFS: CollectingRiders (SRM 382, D1, 250). Hints and no code.
- BFS: FromToDivisible (SRM 699, D1, 500). Hints and no code.
- Dijkstra: ColorfulRoad (SRM 596, D2, 500). Hints and no code.
- Dijkstra: The Topcoder "ThreeTeleports" problem (SRM 519, D2, 600). I give you hints here, and not code.
- Dijkstra: InsertSort (SRM 351, D2, 1000-pointer). Hints and no code.

Breadth First Search (BFS) is complementary to Depth First Search (DFS). DFS works by visiting a node and then recursively visiting children. You can view it as relying on a stack -- push a node onto a stack, then go through the following algorithm:

- Pop a node off the stack.
- Do some processing on the node.
- Push all of the node's children onto the stack.
- Repeat until the stack is empty.

Ex-Graph.txt
NNODES 12 EDGE 8 10 EDGE 4 11 EDGE 4 6 EDGE 3 8 EDGE 0 2 EDGE 2 9 EDGE 5 6 EDGE 4 8 EDGE 1 11 EDGE 6 7 EDGE 0 11 EDGE 3 7 EDGE 3 9 EDGE 8 9 EDGE 6 8 |

A recursive visiting of all nodes using DFS starting with node zero will look as follows:

Were we to print out the nodes, they would be printed out in the order in which they are visited:

0, 2, 9, 8, 10, 3, 7, 6, 4, 11, 1, 5

Node Visited0 2 9 8 10 3 7 6 4 11 1 5 6 4 3 11 | ActionStart Push 11 and 2 Push 9 Push 3 and 8 Push 4, 6, 3 and 10 Do nothing Push 7 Push 6 Push 5 and 4 Push 11 Push 1 Do nothing Do nothing Already visited Already visited Already visited Already visited | Stack0 2, 11 9, 11 8, 3, 11 10, 3, 6, 4, 3, 11 3, 6, 4, 3, 11 7, 6, 4, 3, 11 6, 6, 4, 3, 11 4, 5, 6, 4, 3, 11 11, 5, 6, 4, 3, 11 1, 5, 6, 4, 3, 11 5, 6, 4, 3, 11 6, 4, 3, 11 4, 3, 11 3, 11 11 | Print0 2 9 8 10 3 7 6 4 11 1 5 |

As you see, the order of the nodes is the same as in the recursive case.

Now, breadth-first search works in the same manner, only we use a queue instead of a stack. See how this differs:

Node Visited0 2 11 9 1 4 8 3 8 6 10 3 6 7 5 7 | ActionStart Append 2 and 11 Append 9 Append 1 and 4 Append 8 and 3 Do nothing Append 8 and 6 Append 10, 3 and 6 Append 7 Already visited Append 5, and 7 Do nothing Already visited Already visited Do nothing Do nothing Already visited | Queue0 2, 11 11, 9 9, 1, 4 1, 4, 8, 3 4, 8, 3 8, 3, 8, 6 3, 8, 6, 10, 3, 6 8, 6, 10, 3, 6, 7 6, 10, 3, 6, 7 10, 3, 6, 7, 5, 7 3, 6, 7, 5, 7 6, 7, 5, 7 7, 5, 7 5, 7 7 | Print0 2 11 9 1 4 8 3 6 10 7 5 |

The order of the nodes is now 0, 2, 11, 9, 1, 4, 8, 3, 6, 10, 7, 5.
The algorithm still visits all nodes and edges, but it does so *in order of distance from the starting node.*
Think about it.

- The first two nodes visited are those that are one edge from node 0: nodes 2 and 11.
- Next are the nodes that are two edges away: nodes 9, 1 and 4.
- Next are the nodes that are three edges away: nodes 8, 3 and 6.
- Finally come the nodes that are four edges away: 10, 7 and 5.

- For all nodes, set their backedges to
**NULL**and their distances to*-1*. - Set node 0's distance to zero and put it on the queue.
- Repeat the following:
- Remove a node
*n*from the queue. - For each edge
*e*from*n*to*n2*such that*n2's*distance is*-1*:- Set
*n2's*distance to*n's*distance plus one. - Set
*n2's*backedge to*e*. - Append
*n2*to the queue.

- Set

- Remove a node

The PDF file **BFS-Run.pdf** contains an example from the graph
above. It shows every step along the way. The final state is below:

Once the BFS finishes, we know the shortest distance of every node from node zero, and we can use the backedges to find the paths. For example, the shortest path from node 0 to node 7 is:

- For all nodes, set their backedges to
**NULL**, their distances to*-1*, and their "visited" field to be false. - Set node 0's distance to zero and put it on the multimap.
- Repeat the following:
- Remove a node
*n*from the front of the multimap and set its visited field to true. - For each edge
*e*from*n*to*n2*such that*n2*has not been visited. Let*d*be*n's*distance plus the weight of edge*e*. If*n2's*distance is -1, or if*d*is less than node*n2's*current distance:- If
*n2*was in the multimap, remove it. [* We're going to revisit this below. *] - Set
*n2's*distance to*d*. - Set
*n2's*backedge to*e*. - Insert
*n2*into the multimap, keyed on distance.

- If

- Remove a node

[* Revisiting Here *]: You actually have a choice of whether to remove a node from the multimap or not. It is often easier to code up Dijkstra's algorithm to leave nodes on the multimap rather than remove them. In that case, when a node reaches the front of the multimap for you to process, you need to check its distance versus its key in the multimap. If they differ, you simply ignore the node, because you have processed it already.

The tradeoff is memory and potentially performance vs coding complexity. When you code, it is much easier to leave the node on the multimap. However, if you end up replacing a lot of nodes on the multimap, it can make performance and memory consumption suffer. Ideally, it is better to remove the node before you re-insert it. To do that properly, you need to store an iterator to the node's place in the multiple, in the node's class definition. Think about that, especially if you decide to remove the node in your own implementation. BTW, I do advocate that you try removing the node in your lab. Not only is the code better, but it forces you to think about data structure design..

As an example, suppose we enrich the graph above with edge weights:

Then
the PDF file **Dijkstra-Run.pdf** shows how Dijkstra's
algorithm runs on the graph. In the PDF file, we are removing nodes when we
replace them). When it finishes, here is the state of the system:

As with the BFS run above, you can use the backedges to find the shortest paths. For example, the shortest path from node 0 to node 3 has a distance of 20, and contains the edges: