13.6: Stackless Traversals

Garbage collection routines require the traversal of dynamic data structures that are more general than binary trees, such as list-structures and more complex lists. Algorithms for their traversal are generalizations of those we consider. For increased clarity, we will restrict our discussion to binary tree traversals and present three techniques that do not use extra storage for a stack.

These algorithms may be written assuming a particular binary tree implementation, such as arrays or pointer variables. Instead, to emphasize data abstraction and functional modularity, the following function, which is independent of the tree implementation, has been chosen. The function is a preorder traversal saving the path from the root on the stack.

preorder(pt)

/* Preorder traverses the binary tree. */

binarytreepointer *pt;

{

binarytreepointer null,p,lptr,rptr;

binarytreepointer setnull(),left(),right(),nextsubtree();

stack s;

null = setnull();

>Comment

p = *pt;

setstack(&s);

>Comment

while(p != null)

{

>Comment

process(pt,p);

>Comment

lptr = left(p);

>Comment

if(lptr != null)

{

>Comment

push(p,&s);

>Comment

p = lptr;

}

>Comment

else

{

>Comment

rptr = right(p);

>Comment

if(rptr != null)

{

>Comment

push(p,&s);

>Comment

p = rptr;

}

>Comment

else

>Comment

p = nextsubtree(&s,&p);

}

}

}

For each traversal, the basic concept is described. Then the appropriate implementation for the routines of the procedure is described.

Consider the binary tree of Figure 13.10, and suppose node.p has just been processed during the traversal using the preorder procedure. The stack will contain pointers p1, p2, p3, p4, p5, p6, and p7. At this point, the traversal of the left subtree of node.p4 has been completed. The purpose of the nextsubtree(&s,&p) function in the preorder traversal algorithms is to find node.p4, to allow the traversal to pick up at its right subtree. In general, whenever the stack is not empty, a null p value signifies the completion of the traversal of the left subtree of some node. The traversal must then pick up at that node's right subtree. There may be more than one such node; it is the most recent that must be found. The stack allows this node, and its right subtree, to be found conveniently. Nextsubtree(&s,&p) is called after the terminal node pointed to by p has been processed. It finds the nonnull right subtree that must be traversed next, returns a pointer to it, and updates the stack to reflect the path from that subtree's root to the root of the tree. If no such nonnull right subtree exists, then it returns a null value. Each of the tree traversals we now consider actually uses stack information, but it is, in effect, stored in the tree, eliminating the requirement for additional stack storage. The preorder procedure treats the tree as a data abstraction, so it is independent of the tree implementation.

Figure 13.10 A Binary Tree T

13.6.1 Threaded Trees

13.6.2 Link Inversion

13.6.3 Robson Traversal