6.3 DEPTH-FIRST AND BREADTH-FIRST
Consider the graph in Fig. 6.4. If we begin the depth-first traversal at node 1, we then visit, in order, the nodes 2, 3, 4, 7, 5, and 6 before we reach a dead end. We would then back up to node 7 to find that node 8 hasn’t been visited, but that immediately leads to a dead end. We next back up to node 4 and find that node 9 hasn’t been visited, but again we have an immediate dead end. We then continue to back up until we reach the starting node, and because all nodes adjacent to it have been visited, we are done.
The recursive algorithm for depth-first traversal is
DepthFirstTraversal(G, v) G is the graph
v is the current node Visit( v )
Mark( v )
for every edge vw in G do if w is not marked then DepthFirstTraversal(G, w) end if
end for
This recursive algorithm relies on the system stack of the computer to keep track of where it has been in the graph so that it can properly back up when it reaches dead ends. We could create a similar nonrecursive algorithm by using a stack data structure and pushing and popping graph vertices ourselves.
■ 6.3.2 Breadth-First Traversal
In a breadth-first traversal, we visit the starting node and then on the first pass visit all of the nodes directly connected to it. In the second pass, we visit nodes that are two edges “away” from the starting node. With each new pass, we visit nodes that are one more edge away. Because there might be cycles in
1 2
6 5
7 8 9
3
■ FIGURE 6.4 4 A graph
the graph, it is possible for a node to be on two paths of different lengths from the starting node. Because we will visit that node for the first time along the shortest path from the starting node, we will not need to consider it again. We will, therefore, either need to keep a list of the nodes we have visited or we will need to use a variable in the node to mark it as visited to prevent multiple visits.
Consider again the graph in Fig. 6.4. If we begin our traversal at node 1, we will visit nodes 2 and 8 on the first pass. On the second pass, we will visit nodes 3 and 7. (Even though nodes 2 and 8 are also at the end of paths of length 2, we will not return to them because they were visited in the first pass.) On the third pass, we visit nodes 4 and 5, and on the last pass we visit nodes 6 and 9.
Where the depth-first traversal depended on a stack, our breadth-first tra- versal is based on a queue. The algorithm for breadth-first traversal is
BreadthFirstTraversal(G, v) G is the graph
v is the current node Visit( v )
Mark( v ) Enqueue( v )
while the queue is not empty do Dequeue( x )
for every edge xw in G do if w is not marked then Visit( w )
Mark( w ) Enqueue( w ) end if
end for end while
This algorithm will add the root of the breadth-first traversal tree to the queue but then immediately remove it. As it looks at the nodes that are adja- cent to the root, they will be added to the end of the queue. Once all of the nodes adjacent to the root have been visited, we will return to the queue and get the first of those nodes. You should notice that because nodes are added to the end of the queue, no node that is two edges away from the root will be
considered again until all of the nodes one edge away have been taken off of the queue and processed.
■ 6.3.3 Traversal Analysis
Our goal for these two traversal algorithms was to create a process that would visit each node of a connected graph exactly once. We begin our analysis to see if this has been accomplished. In breadth-first, we moved out from the starting node. We followed all available edges, unless they led to a node we had already visited. Does this cause any problem? Any nodes that could be reached from that node will still be visited but just from a more direct route. Looking back at Fig. 6.4, we see that we didn’t revisit node 8 after nodes 2 or 3. But anything that we could reach from node 8 is already being visited because node 8 was reached in an earlier pass. Looking at it another way, is it possible for there to be a node in a connected graph that wasn’t visited? Because on each pass we take one step from a node visited on the last pass, the only way for a node not to be visited is for it to not be adjacent to a visited node of the graph. But that would mean that the graph is not connected, which contradicts our statement that the graph is connected, so all of the nodes must be visited.
A similar thing is true for a depth-first search. In this case, we travel deeply into the graph until we reach a dead end. Do we then visit all of the remaining nodes in the process of backtracking? Is it possible that we may have missed a node in this process? Each time that we reach a dead end, we back up to the first node that has an unvisited adjacent node. We move to that node and again begin to travel deeply into the graph. A dead end again causes us to back up to the first node with an unvisited adjacent node that we find. For a node in the graph to not be visited at the end of this process means that it must not be adjacent to any of the nodes visited in the graph. But again, this would mean that the graph is not connected, which contradicts our statement that it is. All of the nodes must be visited in this traversal as well.
What about the efficiency of this algorithm? Our assumption is that the work done as we visit each node is the most complex part of this process. So, the work done to check to see if an adjacent node has been visited and the work to traverse the edges is not significant in this case. So the order of the algorithm is the number of times a node is visited. Because we have said that these algo- rithms visit each node exactly one time, for a graph with N nodes, the visit pro- cess will be done N times. These traversals are, therefore, of order O(N).
6.3.4
1. For the following graphs, give the order that the nodes will be first visited when doing a breadth-first traversal starting at the node labeled with a 1.
2. For the graphs in question 1, give the order that the nodes will be first vis- ited when doing a depth-first traversal starting at the node labeled with a 1.
3. Write a detailed algorithm for depth-first traversal using an adjacency matrix that just prints the node label as the visit operation. You should trace it using the graphs in this section to make sure you get the same answer.
4. Write a detailed algorithm for breadth-first traversal using an adjacency matrix that just prints the node label as the visit operation. You should trace it using the graphs in this section to make sure you get the same answer.
5. Write a detailed algorithm for depth-first traversal using an adjacency list that just prints the node label as the visit operation. You should trace it using the graphs in this section to make sure you get the same answer.
6. Write a detailed algorithm for breadth-first traversal using an adjacency list that just prints the node label as the visit operation. You should trace it using the graphs in this section to make sure you get the same answer.
7. Prove that each edge in a connected graph will be part of the depth-first tra- versal tree or will be an edge pointing to a predecessor in the tree.
8. Prove that each edge in a connected graph will be part of the breadth-first traversal tree or will be an edge pointing to a node in the tree that is neither a predecessor or descendent.
6.3.4 EXERCISES
■
1 2
6 7
4
3 5
1 2
6 7
4
5 3
a.
c.
1 2
6 7
4
5 3
1 2
6 7
4
5 3
b.
d.