Professional Documents
Culture Documents
1 of 7
http://www.cs.bu.edu/teaching/c/tree/breadth-first/
<-- root
k
\
h
\
d
The 2 other traversal orders we know are inorder and postorder. An inorder traversal would give us: a, d, f, h, j, k, z. A
postorder traversal would give us: d, a, h, f, z, k, j.
Well, inorder and postorder traversals, like a preorder traversal, also try to go deeper first...
For example, the inorder traversal visits a and d before it explores a's sibling h. Likewise, it visits all of j's left subtree (i.e., "a,
d, f, h") before exploring j's right subtree (i.e., "k, z"). The same is true for the postorder traversal. It visits all of j's left subtree
(i.e., "d, a, h, f") before exploring any part of the right subtree (i.e., "z, k").
3. Breadth-first traversal:
Depth-first is not the only way to go through the elements of a tree. Another way is to go through them level-by-level.
For example, each element exists at a certain level (or depth) in the tree:
tree
---j
/
\
f
/
k
\
<-- level 0
<-- level 1
\
<-- level 2
\
d
<-- level 3
5/7/2013 12:05 PM
2 of 7
http://www.cs.bu.edu/teaching/c/tree/breadth-first/
This level-by-level traversal is called a breadth-first traversal because we explore the breadth, i.e., full width of the tree at a
given level, before going deeper.
Now, how might we traverse a tree breadth-first? We'll need some other mechanism than the ones we've already used since
preorder, inorder and postorder traversals don't produce breadth-first order.
4. Why breadth-first:
You may be thinking: "Why would we ever want to traverse a tree breadth-first?" Well, there are many reasons....
Tree of Officers
Suppose you have a tree representing some command structure:
Captain Picard
/
Commander Riker
/
\
Lt. Cmdr.
Lt. Cmdr.
Worf
LaForge
|
Lieutenant
Cameo-Appearance
\
Commander Data
|
Lt. Cmdr.
Crusher
|
Lieutenant
Selar
This tree is meant to represent who is in charge of lower-ranking officers. For example, Commander Riker is directly
responsible for Worf and LaForge. People of the same rank are at the same level in the tree. However, to distinguish between
people of the same rank, those with more experience are on the left and those with less on the right (i.e., experience decreases
from left to right).
Suppose a fierce battle with an enemy ensues. If officers start dropping like flies, we need to know who is the next person to
take over command. One way to trace the path that command will follow is to list the officers in the tree in breadth-first order.
This would give:
1.
2.
3.
4.
5.
6.
7.
8.
Captain Picard
Commander Riker
Commander Data
Lt. Cmdr. Worf
Lt. Cmdr. LaForge
Lt. Cmdr. Crusher
Lieutenant Cameo-Appearance
Lieutenant Selar
Game Tree
Another time when breadth-first traversal comes in handy is with game trees. Suppose we have a tree for a game of chess. In
other words, levels of the tree alternately represent possible moves by you, and then by your opponent, and then by you...
current state of game
|
move
move
...
queen's
king's
bishop
knight
/
|
\
/
|
\
move move
...
move move ...
king queen's
king king's
rook
knight
|
|
.
.
.
.
.
.
/
\
move
queen
|
...
You have exactly 1 minute to decide on a move. Now, which would be a better use of that time: exploring the branch where
you "move your queen's bishop" to its fullest extent (go deep) or explore each of your possible next move first, and then your
opponent's responses (breadth-first).
In this case, traversing the game tree breadth-first makes more sense than exploring one move infinitely (depth-first) before
exploring another move.
5. Help for breadth-first traversing:
Let's return to example trees that are binary and that just hold characters.
As we've seen, the recursive tree traversals go deeper in the tree first. Instead, if we are going to implement a breadth-first
traversal of a tree, we'll need some help....Perhaps one of the data structures we already know can be of assistance?
5/7/2013 12:05 PM
3 of 7
http://www.cs.bu.edu/teaching/c/tree/breadth-first/
How to use helper data structure: What we'll do is store each element in the tree in a data structure and process (or visit) them
as we remove them from the data structure.
We can best determine what data structure we need by looking at an example:
f
/
h
\
d
When we are at element f, that is the only time we have access to its 2 immediate children, a and h. So, when we are at f, we'd
better put its children in the data structure. Obviously then, f must have been in the data structure before them (i.e., first), since
we'd have put f in when we were at f's parent.
So, if we put the parent in the data structure before its children, what data structure will give us the order we need? In other
words, to explore the tree breadth-first, do we want the children to be removed from the data structure first or the parent to
be removed first?
Answer: A queue will give us the order we want! A queue enforces first-in-first-out order, and we want to process the first
thing in the data structure, the parent, before its descendents.
6. Using one data structure to implement another:
The organization of a program that uses a breadth-first traversal might look like:
main program
------------
tree.h
------
tree.c
------
call
TreeBreadthFirst
TreeBreadthFirst
prototype
TreeBreadthFirst
definition
In other words, the main program needs to call some function that performs a breadth-first traversal, like
TreeBreadthFirst(). This means that the interface (tree.h) must provide a prototype for such a function. Also, the
implementation (tree.c), which the main program has no direct access to, must define the function TreeBreadthFirst().
If we use a queue to help us implement the breadth-first traversal, then we must extend the picture...
...
tree.c
------
queue.h
-------
queue.c
-------
...
TreeBreadthFirst
definition (uses
a queue)
queue funcs.
prototypes
queue funcs.
definitions
The tree implementation will use types and functions from the queue interface. The queue implementation will provide
definitions for those functions, but they are hidden from the user of the queue--here, the user of the queue is the tree
implementation!
Finally, since the main program cannot see the implementation of the tree, it won't even know that a queue is involved and
won't have any access to that queue.
7. Connecting the queue to the tree:
Previously, we've seen that the following organization of types can be used for a tree of characters:
tree.h
------
tree.c
-----#include "tree.h"
#include "queue.h"
The one adjustment needed is that the tree implementation will have to include queue.h, since it uses a queue.
Now, the types for a queue have similar organization:
queue.h
-------
queue.c
------#include "queue.h"
5/7/2013 12:05 PM
4 of 7
http://www.cs.bu.edu/teaching/c/tree/breadth-first/
type-of-an-element
abstract-type-of-a-queue
concrete-type-of-a-queue
Of course, we know what the abstract type will be and that the concrete type will be a structure:
queue.h
-------
queue.c
------#include "queue.h"
typedef ??
queueElementT;
typedef struct queueCDT
*queueADT;
It's actually irrelevant (to those writing the tree) what the internals of the queue are, i.e., what a queueCDT really holds and
what other types it may need for the implementation (e.g., queueNodeTs?).
However, we still must determine what a queueElementT is! This is what will make the queue useful to help traverse a tree.
So, we are really asking: What type of thing should the queue store?
We can answer that question by asking: What type is present throughout the tree that we can use to refer both to the top-level
tree and each and every subtree?
an ADT
|
-------+---> | root | a CDT
| |
|
---+---|
v
----| j |
|---|
| | |
/---\
v
v
--------| f |
| k |
|---|
|---|
| | |
|0| |
/---\
----\
v
v
v
...
...
...
Answer: treeNodeT *! It is the only type that allows us to refer to both the top-level and all subtrees.
Therefore, the queue must be able to store things of type treeNodeT *.
There is more than one way we can achieve this, we could:
Move the type definition for treeNodeT into queue.h. Then we could do:
typedef treeNodeT *queueElementT;
That would work since we'd get the type treeNodeT in tree.c (where we need it) when we include queue.h.
It's not ideal though, since treeNodeT really belongs in the tree implementation file.
Leave treeNodeT in tree.c, but refer to it in queue.h.
Remember that we can refer to a pointer to a structure that is not defined, as in:
typedef struct treeNodeTag *queueElementT;
As long as we don't try to dereference that pointer. That would work because the queue will just store these pointers, not
dereference them.
Finally, we could use the generic pointer type, void *, as the element type.
A void * can take on the value of any pointer (i.e., can store the address of anything), so we could use it to store the
value of a treeNodeT *.
We'll choose the last option, because it produces the most general queue.
5/7/2013 12:05 PM
5 of 7
http://www.cs.bu.edu/teaching/c/tree/breadth-first/
queue.c
------#include "queue.h"
typedef void *
queueElementT;
typedef struct queueCDT
*queueADT;
8. TreeBreadthFirst() function:
Finally, we can implement the function TreeBreadthFirst(), which traverses a tree using a queue to achieve breadth-first
order. Right now, we don't care what it does when its visits an element.
This function will have to receive a tree via an ADT...and doesn't need to return anything, so its prototype looks like:
void TreeBreadthFirst(treeADT tree);
Now, the essence of the algorithm is to use a queue, in other words, to process nodes while there are node pointers left in the
queue still to be processed.
So, the core of the function will be looping through the contents of the queue:
while (!QueueIsEmpty(queue)) {
...
However, pointers to all the nodes in the tree won't be in the queue at once. We'll have to place them in the queue when we
have access to them (i.e., we only get access to a child when we are at its parent).
Here's one solution to the problem:
void TreeBreadthFirst(treeADT tree)
{
/* Temporary queue. */
queueADT queue;
/* Points to node we are processing. */
treeNodeT *traverse;
if (tree->root == NULL)
return; /* Nothing to traverse. */
/* Create a queue to hold node pointers. */
queue = QueueCreate();
/*
* Gotta put something in the queue initially,
* so that we enter the body of the loop.
*/
QueueEnter(queue, tree->root);
while (!QueueIsEmpty(queue)) {
traverse = QueueDelete(queue);
Visit the node pointed to by traverse.
/*
* If there is a left child, add it
* for later processing.
*/
if (traverse->left != NULL)
QueueEnter(queue, traverse->left);
/*
* If there is a right child, add it
* for later processing.
*/
if (traverse->right != NULL)
QueueEnter(queue, traverse->right);
}
/* Clean up the queue. */
QueueDestroy(queue);
}
Notice that, because we can solve this problem with iteration (instead of recursion), we do not need a wrapper function that
pulls out the top-level node pointer from the CDT.
9. Using a stack instead:
5/7/2013 12:05 PM
6 of 7
http://www.cs.bu.edu/teaching/c/tree/breadth-first/
k
\
\
h
\
d
Within each call of Traverse() on a particular element (or whatever an element is held in, e.g., a node, an array element, etc.),
there would be recursive calls to traverse its children. (Below indentation indicates another level of recursion.)
Traverse(j)
Traverse(f)
Traverse(a)
Traverse(d)
Traverse(h)
Each of these recursive calls is a function call. When you call a function, the parameter(s) of the function actually get pushed
onto something called the call stack.
So, the sequence of recursive calls above has the following effect on the call stack:
call
Traverse(j)
j
---------call stack
f
5/7/2013 12:05 PM
7 of 7
http://www.cs.bu.edu/teaching/c/tree/breadth-first/
call
Traverse(f)
j
---------call stack
call
Traverse(a)
a
f
j
---------call stack
call
Traverse(d)
d
a
f
j
---------call stack
return from
Traverse(d)
a
f
j
---------call stack
return from
Traverse(a)
f
j
---------call stack
call
Traverse(h)
h
f
j
---------call stack
...
When the function is called, its parameter(s) go on the top of the call stack, and when the function returns, its parameter(s) get
popped off of the call stack.
In other words, with recursive traversal functions, there is really a stack helping with the traversal.
BU CAS CS - Breadth-First Traversal of a Tree
Copyright 1993-2000 by Robert I. Pitts <rip at bu dot edu>. All Rights Reserved.
5/7/2013 12:05 PM