There is an exercise (ex.2.3.1-21) in TAOCP vol.1:
21. [33] Design an algorithm that traverses an unthreaded binary tree in inorder without using any auxiliary stack. It is permissible to alter the LLINK and RLINK fields of the tree nodes in ny manner whatsoever during the traversal, subject only to the condition that the binary tree should have the conventional representation illustrated in (2) both before and after your algorithm has traversed the tree. No other bits in the tree nodes are available for temporary storage.
In the answer given by Prof. Knuth, an algorithm by Joseph M. Morris [Inf. Proc. Letters 9 (1979), 197-200] is introduced. The algorithm is very interesting. I'd like to show you how it works here.
First let's see how to do an inorder traversal using a stack:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | void inorder_traverse(NODE * T)
{
NODE * p = T;
while (1) {
while (p) {
PUSH(p);
p = p->L;
}
if (!(p = POP()))
break;
visit(p);
p = p->R;
}
}
|
This function can be illustrated as:
The key points are:
Function inorder_traverse is not hard to understand. You should have known well about it. Now let's see how Morris modified it so that no stack is needed any more.
The following is the pseudo-C code of Morris Algorithm:
The key point is to transform the binary tree into a right-threaded binary tree temporarily and restore it later (line 7, 8, 9, 10).
A right-threaded binary tree, if you never heard of it, is a binary tree like this:
As this figure shows, the NULL right child of a node P is used to point to P's inorder successor (denoted as P$). That is to say:
Let's study figure Right-threaded binary tree, which is transformed from (2). There are 4 nodes that have left subtrees: A, B, C and F, so there are 4 thread that point to them. The key point is that the nodes pointing to them are their inorder predecessors respectively: B, D, G and H. Therefore, we do not need to push A, B, C and F into stack, since we can reach them via the right links of their inorder predecessors.
This is the main idea of Morris algorithm. Now let's rewrite our inorder_traverse so that it looks more like inorder_Morris:
This is a disguised inorder_traverse. It does the same thing as the first inorder_traverse, but it looks very similar with inorder_Morris so we only need to do very slight modification and an implementation of Morris algorithm will be done.
Before implementing the Morris algorithm, let's imagine all threads are inserted into the tree. It's easy to know:
line 15 is always true (except that we have reached the last node)
if the line from p to p->R is a thread, then p->R always points to the stack top, so we no more need a stack
to implement Morris algorithm, we only need to replace line 7 and 9 with insert_thread code and remove_thread code respectively and we're done
if a node has no left subtree, visit it without pushing or poping
I guess you have understood Morris algorithm well, it's time to code it:
The code implements not only inorder traversal but also preorder traversal. It is not hard with the help of right threads. The key parts are U4, U5 and line 41.
I made a slides showing how this algorithm works. Please click here (it contains many figures, you may have to wait a while when it's loading).