Download Threaded Trees Institute of Lifelong Learning, University of Delhi

Survey
yes no Was this document useful for you?
   Thank you for your participation!

* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project

Document related concepts

Linked list wikipedia , lookup

Quadtree wikipedia , lookup

B-tree wikipedia , lookup

Lattice model (finance) wikipedia , lookup

Red–black tree wikipedia , lookup

Interval tree wikipedia , lookup

Binary tree wikipedia , lookup

Binary search tree wikipedia , lookup

Transcript
 Threaded Trees
Computer Science B.Sc
Paper No and Name: CSHT203: Data Structures
Unit No : VI Tree Structures
Chapter No and Name: 1 Threaded Trees
Fellow: Dr. Anita goel, Associate Professor
College/Department: Dyal singh College, University of Delhi
Author:- Nidhi Arora,Assistant Professor
College/Department: Kalindi College , University of Delhi
Reviewer:Dr. Vasudha Bhatnagar, Associate Professor
Collge/Department: Dept. of Computer Science , University of Delhi
Institute of Lifelong Learning, University of Delhi
Threaded Trees
Table of Contents
Chapter 1 : Threaded Trees
1.1 : Recursive Inorder :Simulation
1.1.1: short insight into major tree traversals
1.1.2: Depth First traversal :Inorder
1.1.2.1: Recursive Inorder
1.1.2.2: simulating recursive inorder
1.1.2.3: pictorial representation of run time stack
1.1.3: Non recursive inorder : a step further
1.2: Non Recursive In-order and threaded trees
1.2.1: Iterative In-Order Traversal
1.2.1.1 Implementation
1.2.2: Comparison of inorder1 and in-order2
1.2.3 Removal of stack
1.2.4 Threaded tree : A solution
1.2.4.1 Formal Definition
1.2.4.2 Advantages Of Threaded Binary trees
1.3: Threaded Trees : Data Structure and Traversal
1.3.1: Threaded Trees : Data Structure
1.3.1.1 : Double Threaded Trees
1.3.1.2 : Single Threaded Trees
1.3.2: Algorithmic Logic: In-Order
1.3.2.1 Revising In-Order
1.3.2.2 Developing Algorithm
1.4: Threaded Trees : Traversal Implementation
1.4.1: Implementation using right-in threaded tree
1.4.1.1 : In-Order Implementation
1.4.1.2 : Pre-order Implementation
1.4.2: Implementation using Left-in threaded tree
1.4.2.1 Reverse In-Order
1.5: Threaded Trees : Creation and Applications
1.5.1: Constructing a Right-In Threaded Binary tree
1.5.1.1 : Main Function
1.5.1.2 : Sub Functions
1.5.2: Applications of threaded trees
Summary
Exercise
Glossary
References
Institute of Lifelong Learning, University of Delhi
Threaded Trees
Threaded Trees
The chapter is divided into five major sections. First two sections i.e. section 1.1 and 1.2
discusses the recursive and non recursive inorder traversal methods of Binary tree in
detail. It also discusses how the drawbacks of these two have led to the discovery of
threaded tree based traversal methods. Then the next two sections i.e. 1.3 and 1.4
discuss the data structure needed to define a threaded binary tree and various traversals
with different kind of threaded trees respectively. In these two sections the algorithmic
logic along with their c implantations are discussed rigorously. The last section i.e. section
1.5 focuses on the logic of creating a threaded binary tree and their applications. The
summary, exercise glossary and references are given in the last
1.1 : Recursive Inorder :Simulation.
1.2: Non Recursive In-order and threaded trees.
1.3: Threaded Trees : Data Structure and Traversal.
1.4: Threaded Trees : Traversal Implementation.
1.5: Threaded Trees : Creation and Applications.
1.1 Recursive In order: Simulation
Before going into the intricacies of threaded trees let us review basic traversal methods for
binary trees, which led for the discovery of threaded trees.
1.1.1 Need for Threaded Trees
Tree traversal as the name suggests is visiting every node once without restricting on the
order of the nodes to be visited. So a tree with n number of nodes can be traversed in n!
number of ways. Oh…. That’s a big number. But only few out of n! traversals can be used
for real data. Rest all appears to be useless as they only represent the data in a
1. Depth first traversal (Preorder, Inorder, Postorder)
2. Breadth first traversal(Level by Level Traversal)
You must have got a taste of all these well known and well used traversal methods in the
previous chapters. Breadth first uses queue data structure and Depth first traversals use
stacks heavily either implicitly in recursive implementation or explicitly in iterative
implementation. Depth first traversals due to heavy use of stacks put lot of overhead on
the overall execution of such traversals. It seems that the use of stacks is unavoidable in
all the Depth first traversals, but is it really so? Can’t we do something to avoid the use of
this heavy overhead giving data structure and make it more efficient in terms of running
time and space? Let us see…….
Lot of research has been done in this area. Specifically people have tried to make inorder
as stack less as it is used mostly to traverse a sorted binary tree in sorted manner. Such
methods with slight modifications can be applied for other kind of depth first traversals as
well. Threaded trees are one such way to do stack less inorder traversal.
Institute of Lifelong Learning, University of Delhi
Threaded Trees
So now we will be concentrating on inorder depth first traversal as it is mostly used for
binary trees (specifically binary search trees) to print the data in sorted order and see how
it can be modified to eliminate stack to make it more efficient. Wow what a great idea it
seems … But for eliminating stack from inorder we must first understand properly it’s
working for both recursive inorder and non recursive inorder. So let’s go
FAQs
Which traversal method would be used ?
Which traversal method would be used to print an expression of
the expression tree and which is used to evaluate it?
Arithmetic Expressions Tree ((2+2)+(2+2))+(3+3)
Answer: In order traversal will be used to print the expression and Post Order will be
used to evaluate the expression.
1.1.2 Depth First Traversal : INORDER
1.1.2.1 Recursive Inorder
1.1.2.2 Simulating Recursive Inorder
1.1.2.3 Pictorial Representation of Run Time Stack
1.1.2.1 Recursive Inorder
To traverse a non-empty binary tree in inorder, perform the following operations
recursively at each node:
1. Traverse the left sub tree.
2. Visit the root.
3. Traverse the right sub tree.
This can be written as LVR in short form that means go to Left Visit Right. This order
prints the data in binary search trees where left child is less than the root and right child is
greater than the root. As it is seen from the definition once the node is reached and if it
has any right child, inorder traverses it first, leaving that node on waiting state. After the
left child is traversed the algorithms comes back to the node which initiated the call. This
Institute of Lifelong Learning, University of Delhi
Threaded Trees
is known as backtracking. And for backtracking algorithms recursion is an obvious choice.
Here is a simple recursive function inorder1( ) given as Algorithm1 drawn from the
definition directly.
Algorithm 1 : Recursive Inorder
Void inorder1( node *tree)
{ if (tree !=NULL)
/*1*/ {inorder1(tree->left);
/*2*/cout<<tree->info;
/*3*/inorder1(tree->right);
/*4*/ }
Isn’t it simple….. ! Obviously yes. But wait it is simple for the programmer but system has
to do lot more work than it is being shown here. We already know that where ever
recursion is there system manages interaction between recursive calls by using runtime
stack. The system will manage the two recursive calls by implicitly creating a stack and
pushing and popping the activation records which are being created for the recursive calls.
Did You Know?
Tree Recursion is different from recursive tree traversal
Tree recursion is a common pattern of computation. As an example, consider computing
the sequence of Fibonacci numbers, in which each number is the sum of the preceding
two:
0,1,1,2,3,5,8,13,21,...
In general, the Fibonacci numbers can be defined by the rule
We can immediately translate this definition into a recursive procedure for computing
Fibonacci numbers:
Consider the pattern of this computation. To compute (fib 5), we compute (fib 4) and (fib
3). To compute (fib 4), we compute (fib 3) and (fib 2). In general, the evolved process
looks like a tree which we call recursive tree as shown in following figure.
Institute of Lifelong Learning, University of Delhi
Threaded Trees
but recursive tree traversal is a method of visiting every node of the tree in recursive
manner.
1.1.2.2 Simulating Recursive Inorder
For simulating the behavior of recursive inorder we will take up an example tree as shown
below. First of all we see how on different sub trees of the main tree, recursive calls are
generated.
We will consider the Binary search tree of Figure 1.1.
Figure 1.1 Binary Search Tree
Before viewing the run time stack data structure, the output given by the above binary
tree is explained in the following manner:
Institute of Lifelong Learning, University of Delhi
Threaded Trees
Figure 1.2 Simulation of Recursive Calls
The circles in the figure 1.2 show how the recursive calls have been made.
•
•
•
•
•
•
•
Node (15) is the root on which inorder1() is called for the first time.
The function calls itself for node 15’s left child i.e. node 4.
Node (4) left child is not null, so inorder1() is called on node 1.
Because node (1) is a leaf, inorder is called on its null left sub tree which finishes
immediately and node 1 is printed on screen.
nvocation on its right null sub tree also finishes quickly and doesn’t yield any
subsequent recursive calls.
It then resumes call to node 4 by printing its value on screen and then a recursive
call to its null right child is made which also finishes quickly and node (15) value is
printed on screen.
Then inorder1() is called for node (20) which is the right child of node (15) and so
on as clear from the figure 1.2.
The inorder traversal thus is: 1 4 15 16 20 25 Now we will analyze how it is done
using Run time stack which will help us to view its heavy involvement in the
above recursive algorithm
Did you Know?
Tree rotations are inorder invariant
The right rotation operation as shown in the image above is performed with Q as
the root and hence is a right rotation on, or rooted at, Q. This operation results
in a rotation of the tree in the clockwise direction. The symmetric operation is
the left rotation which results in a movement in an anti-clockwise direction (the
left rotation shown above is rooted at P).
Institute of Lifelong Learning, University of Delhi
Threaded Trees
The tree rotation renders the inorder traversal of the binary tree invariant.
This implies the orders of the elements are not affected when a rotation is
performed in any part of the tree. Here are the inorder traversals of the trees
shown above:
Left tree: ((A, P, B), Q, C)
Right tree: (A, P, (B, Q, C))
Computing one from the other is very simple.
Tree rotations are used in a number of tree data structures such as AVL
trees,Red Black trees , splay trees etc. to balance the tree
They require only constant time because they are local transformations: they
only operate on 5 nodes, and need not examine the rest of the tree.
1.1.2.3 Pictorial Representation of Run Time Stack
We will refer here inorder1() given in the previous section for better understanding of
recursive call timings and run time stack layout at every step.
Line numbers 1, 2, 3 and 4 are given as a comment on the left side of the statements. We
will be showing the stack state with activation records which are being created with each
recursive call
In an activation record we are showing two things, the node’s value is written first on
which recursive call is made and adjacent to it the return address is written in the braces.
This is a very important piece of information as whenever a function is invoked its return
address to its caller is also stored in the activation record thus created.
Institute of Lifelong Learning, University of Delhi
Threaded Trees
Explanation
1. Initially the stack is empty. Node 15 is the root so inorder is invoked on node 15 by
the caller .activation record is pushed at the top with value and 15 and return
address as caller.
2. if statement returns true for this node and statement numbered 2 is executed
which returns for another recursive call to inorder on node 4 which is the left child
of node 15 , leaving statement 2 as pending. So a new activation record is pushed
on the stack with respect to current invocation with node value as 4 and return
statement address as 2 in the previous activation record.
3. For this recursive call on node 4, if statement is executed and another recursive
call on node 4th left child is done i.e. on node 1, leaving statement number 2
pending. So we can see a new activation record on the top with value 1 and return
address to statement number 2 in the previous activation record.
4. For this new recursive call again if statement is executed , which results in the new
call to inorder at statement 1 on a null left child of node 1 , so we can see a new
activation record with null as node’s value and statement 2 as return address in the
previous activation record.
5. This call to null node will result in immediate termination as if statement is only
executed if a node is not null. In this no new recursive calls are done and this
activation record is removed and control transfers to previous activation record at
statement number 2 printing node 1‘s value and then executing statement number
3.
6. At this stage a new activation record on node 1’s right child is done which is again
null. So null (4) is there on the top of the stack in which (4) is the return address
of the statement which is pending in the previous activation record. This call also
finishes immediately, without inserting any more activation records.
7. Statement (4) is resumed for node 1’s activation record, finishing it also and thus it
is removed and statement 2 of its previous activation record is executed printing
node 4 value on screen. Then statement 3 for this execution will result in a new
activation record for node’s 4th right child which is again null.
8. In the same manner the process will continue until the overall stack of activation
records is not empty.
Institute of Lifelong Learning, University of Delhi
Threaded Trees
Activity
Pre order traversal and Post order traversals
1. Draw the pictorial stack representation of recursive post order and recursive
preorder traversals at each step of execution.
2. Count the number of push and pop operations done.Are they different from in
order?
Activity
Inclusion of parent field in every node: A different idea for traversal.
Assume that every node of the Binary tree contains a Parent field apart from other data
variables i.e data, left pointer and right pointer. This parent field stores the address of
parent in every node.
1. Will it be possible to traverse the tree in inorder using only this information
without using stack?
2. If some other set of information needs to be stored in every node, what would be
that?
3. Is this version more efficient than recursive inorder?
Try it yourself to find the answer.
1.1.3 Non Recursive Inorder : A step further
In the previous discussion of recursive inorder, we have seen how a runtime stack is used
heavily for pushing and popping of activation records.
Now we will try to write its iterative counterpart and let’s see are we able to write it more
efficiently or not. Whether we would be able to use less data structures such as stack etc?
These are some of the questions we would be taking in the next chapter which will
concentrate in inorder iterative version, their comparisons and finding out other ways to
overcome the shortcoming of recursive and non recursive inorder algorithms
1.2 Non Recursive In-order and its Implications
In the last section we have discussed the recursive version of in-order traversal. Through
its simulation we have got an idea of how it is heavily using the stack, which obviously
puts lot of overhead on the overall execution of the algorithm.
1.2.1 Iterative In-order Traversal
In this section we will try to see the iterative counterpart of recursive in-order. While
looking at its iterative counterpart we have few questions in our mind like, whether stack
can be removed in iterative version. If not, then is iterative version of in order more
efficient than the recursive version. Whether there is some another means of doing inorder traversal without using stack data structure at all. Let’s try to dig an answer for all
these questions through this discussion.
1.2.1.1 Implementation
In-order traversals in a tree are based on the following logic “traverse extreme left as far
as possible while saving the pointers to the nodes which are passed in the way so as to
visit them again while backtracking”. This is accomplished automatically and implicitly by
using a run time stack in recursive in-order i.e. inorder1 () which we have already
Institute of Lifelong Learning, University of Delhi
Threaded Trees
discussed in the last section. But in iterative inorder which we will refer as inorder2 ()
here, we have to use a stack explicitly for this purpose. Let’s see.
In the following function a stack is being defined as a class and an object named‘s’ of the
stack is used to push and pop the elements coming on the way of traversal. The stack
defined here is a stack of nodes of type NODEPTR . For sake of simplicity we are assuming
the NODEPTR as pointer to any node of the tree which contains info, left and right pointer.
Also we are assuming the functions push and pop as part of our stack used to push a node
pointer on stack and pop a node pointer out of the stack respectively. The implementation
details about the same can be find in previous chapters.
Algorithm 2: Non Recursive Inorder
#define MAXSTACK 100
class stack
{
int top;
NODEPTR item[MAXSTACK];
public :
void stack()
{ top=-1;
}
void push(NODEPTR x);
NODEPTR pop();
}s;
void inorder2(NODEPTR root)
{
NODEPTR p;
p=root;
/*1*/ do {
/*travel down left branches as far as possible*/
/* saving pointers to nodes passed */
/*2*/
while (p!=NULL)
/*3*/
{
/*4*/
s.push (p);
/*5*/
p=p-<left;
/*6*/
}
/*end while*/
/*check if finished*/
/*7*/
if (!s.empty()){
/*at this point the left subtree is empty*/
/*8*/
p=s.pop();
/*9*/
printf(“%d”,p->info);
/*10*/
p=p->right;
/*11*/
}
/*12*/
}while (!s.empty () || p!=NULL);
/*13*/
}/*end intrav2
The logic behind inorder2 is as follows:
Initially the stack is empty and p points to root of the tree. The outer do-while loop is
entered at statement number 1 and exited when the stack is empty. Traverse the left
nodes of p starting at p and pushing each left node coming on the way on the stack s till
you reach null node in statement numbered 2-6(inner while loop).Now pop a node and
print it. Make p point to its right node. If p is empty that means right sub tree is empty
and we don’t have the right sub tree to traverse so repeat the process of popping another
node from the stack..If p is non empty then repeat the in order traversal process for this
node i.e. pushing the left nodes till we reach null.
We can very well see that the inorder2 uses stack heavily in its implementation.
Institute of Lifelong Learning, University of Delhi
Threaded Trees
FAQs
What is the time and Space requirement for an in-order traversal?
The time required is O ( n) as each node has to be visited at least
once so the while loop will be executed n times .
The space required is O ( n) in the worst case when the tree is not
balanced and O(log n) on an average when the tree is balanced.See
for example the tree given below.
In the above tree where there are no right nodes, all the nodes have to be pushed on
the stack to reach the leftmost child. Hence the space requirement is O( n) in this case.
In the above complete binary tree, all the nodes of the left most paths need to be
pushed on the stack. i.e. space requirement is O(log n) hence O ( h).
Institute of Lifelong Learning, University of Delhi
Threaded Trees
1.2.2 Comparison of inorder1() and inorder2()
Both the algorithms incur a running time of O(n) for n nodes tree, but stack operations
increase the overall constant factor added to this.
We tried to convert the recursive in-order to iterative in-order keeping in mind that it will
help us in reducing the overhead incurred by recursive version. But it seems that we have
not accomplished what we wanted to do, as it is clear from the iterative implementation
that stacks can not be avoided with present scenario. In both the algorithms, stacks are a
major issue. But apart from this issue, let’s now compare which version is more easy and
efficient for us to use.
inorder1 ()
inorder1 uses implicit recursion stack very efficiently. Two reasons are listed as :
1. Even if we see two recursive calls in inorder1, both are not recomputed as is done
in Fibonacci.
2. There are no extra parameters to the recursive calls done which is again reducing
the overhead.
3. The recursive algorithm is easier to understand as it is implied directly on its
definition of Left Visit Right order.
inorder2 ()
1. In inorder2(), the calls to push(), pop() and isempty() are done again and again
2. If we see the implementations of push() , pop() and isempty() as discussed in the
previous chapters , we can see the overflow checks every time we try to push a
new element and underflow checks every time we try to pop a element from the
stack . Such checks on each call will slow down the execution speeds.
Under these ideas we can say that recursive inorder1 should be used as compared to
iterative version i.e. inorder2.
1.2.3 Removal of stack
It's really too bad that we need such stacks for traversal. First, they take up space.
Second, if an item is inserted into or deleted from the tree during traversal, or if the tree
is balanced, we have to rebuild the traversal stack. In addition, it can sometimes be
difficult to know in advance how tall the stack will need to be.
Now the question arises. Can we do something to traverse a tree in inorder efficiently
without using stack? In other words can we do stack less depth first traversal? If we can
do this then definitely we will be improving the run time of the overall algorithm, which
will lead us to a more efficient way of traversing the tree.
The answer to the above question is yes. In 1968; Knuth posed the problem of designing
a stack-free, tag-free, non-recursive algorithm that traversed the tree in in-order while
leaving it unaltered in the end. Since then, numerous solutions have appeared in the
literature. Lot of research has been done in this area and researchers have come out with
lot many solutions and one of them is Threaded trees which is our main area of discussion
in the coming sections of this chapter .
Institute of Lifelong Learning, University of Delhi
Threaded Trees
Biography
Donald Knuth
Donald Ervin Knuth(born January 10, 1938) is a
renowned computer scientist and Professor Emeritus of
the Art of Computer Programming at Stanford
University.
Author of the seminal multi-volume work The Art of
Computer Programming, Knuth has been called the
"father" of the analysis of algorithms, contributing to
the development of, and systematizing formal
mathematical techniques for, the rigorous analysis of
the computational complexity of algorithms, and in the
process popularizing asymptotic notation.
In addition to fundamental contributions in several branches of theoretical
computer science, Knuth is the creator of the TeX computer typesetting system,
the related METAFONT font definition language and rendering system, and the
Computer Modern family of typefaces.
A writer and scholar, Knuth created the WEB/CWEB computer programming
systems designed to encourage and facilitate literate programming, and designed
the MMIX instruction set architecture.
Known for
The Art of Computer
Programming
TeX, METAFONT
Knuth–Morris–Pratt
algorithm
Knuth–Bendix completion
algorithm
MMIX
Notable awards
Turing Award (1974)
John von Neumann Medal
(1995)
Harvey Prize (1995)
Kyoto Prize (1996)
1.2.4 Threaded trees : A solution
A threaded tree is not something different than a binary tree but it is an extension to
simple binary tree. They have evolved from binary trees by using some NULL pointers
(more efficiently) of the binary trees. The main idea behind threaded trees is:
•
•
•
•
Binary trees have a lot of wasted space: the leaf nodes each have 2 null pointers.
We can use these pointers to help us in in-order traversals.
We make the NULL pointers reference the next node or previous node in an in
order traversal; called threads.
We need to know if a pointer is an actual link or a thread, so we keep a boolean for
each pointer in the node itself.
The tree which has such threads in place of it NULL pointers is known as threaded tree.
Institute of Lifelong Learning, University of Delhi
Threaded Trees
A threaded tree is a concept which utilizes the empty right and left pointers in a binary
tree to resolve this problem of stack less depth first traversal of the binary tree.
FAQs
How many Null pointers are there in a tree with n nodes?
There is n+1! Null pointers in a tree with n nodes which will represent a considerable
amount of space wastage.
Stackless in-order traversal By Morris Inorder Algorithm
This algorithm traverses the tree by doing tree transformation. This is a stackless
traversal which does not require any additional storage in the node like storing parent
pointers in a node. However the tree on which we are doing Morris in-order traversal
will temporarily loose its structure which needs to be restored before traversal is
finished. This is a simple and elegant algorithm devised by Joseph M. Morris in 1979.
The idea is if a tree is a degenerate tree i.e. it doesn’t have any left child, then
traversal will become very simple as no left child needs to be considered and
three steps of in-order i.e. LVR will reduce to VR (visit the node , Go Right).
information needed to be retained on the stack about a node before traversing its
child simply because there is no left child.
it’s
the
No
left
So Morris algorithm do temporary transformation so that the node being processed
has no left child and so no information needs to be retained on the stack about that
node. The algorithm can be summarized in the following figure and algorithm is given
next:
Institute of Lifelong Learning, University of Delhi
Threaded Trees
Morrisinorder()
While not finished
If node has no left descendent
Visit it;
Go to right;
Else make this node right child of the rightmost node in it’s left descendent;
Go to this left descendent;
This algorithm successfully traverses the tree but only once. Therefore some
information has to be retained to allow the tree to restore its original form. This is
achieved by retaining the left pointer of the node moved down to its right sub tree as
in the case of nodes 10 and 5 in the figure shown here in the above figure.
1.2.4.1 Formal Definition
A threaded binary tree may be defined as follows:
"A binary tree is defined as threaded by making all right child pointers of a node that
would normally be null to point to the inorder successor of that node, and all left child
pointers that would normally be null to point to the inorder predecessor of the node."
There can be two types of threaded binary tree:-
Institute of Lifelong Learning, University of Delhi
Threaded Trees
1) Single Threaded: - i.e. nodes are threaded either towards its in-order predecessor or
successor as we require only one thread at a time to traverse a tree either successor or
predecessor.
Single Left Threaded: - Figure 1.3 shows left null pointers to point to the predecessor of
that node in inorder traversal. This can be used for reverse of inorder traversal i.e. Right
Visit Left for any subtree. i.e. first go right, then visit root and go to left of the
root.
Figure 1.3 Single Left Threaded Tree
Single Right Threaded: - Figure 1.4 shows right null pointers to point to the successor of
that node in inorder traversal. This is also called right-in threaded trees
Figure 1.4 Single Right Threaded Tree
) Double Threaded: - i.e. nodes are threaded towards both the in-order predecessor and
successor. Figure 1.5 showing a Double threaded tree is given below. Both right and left
null pointers are used to point to inorder successor and predessor respectively.
Figure 1.5 Double threaded tree
Did you Know?
The basic difference between a binary tree and the threaded binary tree is that in the
binary trees the nodes are null if there is no child associated with it and so there is no way
to traverse back.
Institute of Lifelong Learning, University of Delhi
Threaded Trees
But in a threaded binary tree we have threads associated with the nodes i.e they either
are linked to the predecessor or successor in the inorder traversal of the nodes. This helps
us to traverse further or backward in the inorder traversal fashion.
Did you Know?
What is the difference between thread pointers and tree pointers in a tree?
Threads are not structural pointers in the tree. They can be removed and tree does not
change but tree pointers are structural pointers which hold the nodes of the tree together.
Did you Know?
Threaded trees are not very common.
Threaded trees are not very common, but the most common of them will only allow
threads for the right links, as shown above, thus allowing an in-order traversal in
ascending sorted order, but to traverse in descending sorted order, we need to use one of
the other solutions for a traversal (parent pointers, recursion, or a stack). In Bithreaded
trees, threads are very difficult to manage and code becomes very complex.
1.2.4.2 Advantages of Threaded Binary Trees
•
A threaded binary tree makes it possible to traverse the values in the binary tree
via a linear traversal that is more rapid than a recursive in-order traversal or
iterative one which uses stacks.
•
It is also possible to discover the parent of a node from a threaded binary tree,
without explicit use of parent pointers or a stack. This can be useful where stack
space is limited, or where a stack of parent pointers is unavailable (for finding the
parent pointer via DFS).This is possible, because if a node (k) has a right child (m)
then m's left pointer must be either a child, or a thread back to k. In the case of a
left child, that left child must also have a left child or a thread back to k, and so we
can follow m's left children until we find a thread, pointing back to k. The situation
is similar for when m is the left child of k.
•
Any node can be accessed from any other node. Threads are usually more to
upward whereas links are downward. Thus in a threaded tree, one can move in
either direction and nodes are in fact circularly linked. This is not possible in
unthreaded counter part because there we can move only in downward direction
starting form root.
Did you Know?
Draw backs of Threaded Trees.
1. Slower tree creation and updations since threads need to be maintained.
2. In theory, threaded trees need two extra bits per node to indicate whether each
child pointer points to an ordinary node or the node's successor/predecessor node.
3. Insertion into and deletions from a threaded tree are all although time consuming
Institute of Lifelong Learning, University of Delhi
Threaded Trees
operations (since we have to manipulate both links and threads).
1.3 Threaded Trees : Data Structure and Traversal
In the last two sections we have got an idea of how stacks are extensively used when
doing depth first in order traversals either recursively or non recursively and threaded
trees as one of the solutions to traverse in depth first manner but without using stack.
Let’s now see how are these threaded trees are implemented.
1.3.1 Threaded Tree: Data Structure
A tree can be double threaded or single threaded. Thread can be inserted in any node
whose left child or right child’s are null so to utilize this wasted space. So in any node ,a
right link can point to either a right child or if no right child is present it can point to its
inorder successor. In the previous case we will call this a tree link and in the later case we
will call this a thread link. So a thread link (pointer) must be differentiable from a tree
link. So some extra information needs to be inserted in the node to easily see if a node
right child pointer is pointing to its right child or its successor. Similar is the case with left
child pointers.
As we know that a tree can be double threaded or single threaded. Let’s first discuss the
case of bi threaded trees in which where both right and left null pointers in any node can
be used to as a pointer to in-order successor and predecessor.
Misconception
Threads and Multitasking
This usage of "thread" has nothing to do with the idea of a program with multiple "threads
of execution", a form of multitasking within a single program.
1.3.1.1 Double Threaded Trees
In double threaded trees we need to add two logical fields named rthread and lthread to
differentia whether a right pointer is treated as a right child or right thread. rthread is also
treaded same. The logical field rthread is set to true in a node if that node right child
pointer is actually a thread to its in-order successor and false if it’s a pointer to its right
child not the successor. Similarly lthread is set to true in a node where left child pointer is
actually a pointer to its in-order predecessor and not the left child and false otherwise. Let
us see how a node’s structure looks like in bi threaded binary trees……
struct nodetype
{int info;
struct nodetype *left;
struct nodetype *right;
boolean rthread,lthread;
}
Institute of Lifelong Learning, University of Delhi
Threaded Trees
Figure 1.6 Structure of a node for Double threaded tree
A node in a double threaded tree here is represented in C using a structure in Fig 1.6
.Node here contains a info part, a pointer to its left child, a pointer to its right child. It also
contains two logical variables named rthread and lthread. If a node has a right child then
the rthread is set to false for that node and if a node has no right child and the right child
pointer is actually a pointer to its successor then it is set to true in that node.
Similar is the case with lthread logical value which is true if the left child is null and the
pointer is used to point to that nodes in-order predecessor. If a node has a left child then
it is set to false which means the left child pointer is not a thread to its predecessor but it
is a pointer to its actual left child.
Double threaded trees are done to utilize wasted space by null pointers to its fullest, but
maintaining both the threads will make insertions and deletions little complex. We can do
our traversals by maintaining either predecessor threads or successor threads also, thus
reducing the complexity of insertions and deletions in a threaded binary tree.
Did you Know?
Tree rotations are inorder invariant
The right rotation operation as shown in the image above is performed with Q as the root
and hence is a right rotation on, or rooted at, Q. This operation results in a rotation of the
tree in the clockwise direction. The symmetric operation is the left rotation which results in
a movement in an anti-clockwise direction (the left rotation shown above is rooted at P).
The tree rotation renders the inorder traversal of the binary tree invariant.
This implies the orders of the elements are not affected when a rotation is performed in
Institute of Lifelong Learning, University of Delhi
Threaded Trees
any part of the tree. Here are the inorder traversals of the trees shown above:
Left tree: ((A, P, B), Q, C)
Right tree: (A, P, (B, Q, C))
Computing one from the other is very simple.
Tree rotations are used in a number of tree data structures such as AVL trees,Red Black
trees , splay trees etc. to balance the tree
They require only constant time because they are local transformations: they only
operate on 5 nodes, and need not examine the rest of the tree.
1.3.1.2 Single Threaded Trees
We have seen in the previous chapter to traverse a tree in in-order we require only either
a successor thread or predecessor threads. Successor threads give us in-order traversal
and predecessor threads give us reverse of in-order traversal. Pre order and post order
traversals can also be done on a tree using single threads.
Fig 1.7 shows tree with successor threads only and structure of a node for such a single
threaded tree.
struct nodetype
{int info;
struct nodetype *left;
struct nodetype *right;
boolean rthread,lthread;
}
Figure 1.7 Structure of a node for single threaded tree.
In the above figure which uses single right threading i.e. right in threaded trees , their
structre uses only one logical variable named rthread, whose true value represent the
presence of link(thread) to its successor and absence of right child. And its false value
shows presence of right child and absence of pointer to its successor.
We can define single left threaded tree which is left as an exercise for the reader. Right in
threaded trees are first introduced by A.J. Perlis and C. Thronton in 1960 in their research
paper “symbol manipulation by threaded trees.”
FAQs
Usefulness of left threaded trees.
Question: We can define a left-threaded tree in a way analogous to a right-threaded tree,
Institute of Lifelong Learning, University of Delhi
Threaded Trees
as a binary search tree with threads only on the left sides of nodes. Is this a useful thing
to do?
Answer: If we already have right-threaded trees, then we can get the benefits of a leftthreaded tree just by reversing the sense of the comparison function, so there is no
additional benefit to left-threaded trees.
Biographic Sketch
Alan Jay Perlis
Alan Jay Perlis (April 1, 1922 – February 7, 1990) was an American computer scientist
known for his pioneering work in programming languages and the first recipient of the
Turing Award.
Perlis was born to a Jewish family in Pittsburgh, Pennsylvania. In 1943, he received his
bachelor's degree in chemistry from the Carnegie Institute of Technology (now Carnegie
Mellon University). During World War II, he served in the U.S. Army, where he became
interested in mathematics. He then earned both a master's degree (1949) and a Ph.D.
(1950) in mathematics at MIT. His doctoral dissertation was titled "On Integral
Equations, Their Solution by Iteration and Analytic Continuation".
In 1952, he participated in Project Whirlwind. He joined the faculty at Purdue University
and then moved to the Carnegie Institute of Technology in 1956. He was chair of
mathematics and then the first head of the Computer Science Department. He was
elected president of the Association for Computing Machinery in 1962.
He was awarded the Turing Award in 1966, according to the citation, for his influence in
the area of advanced programming techniques and compiler construction. This is a
reference to the work he had done as a member of the team that developed the ALGOL
programming language.
In 1971, Perlis moved to Yale University to become the chair of Computer Science and
hold the Eugene Higgins chair. Perlis was elected to the National Academy of
Engineering in 1977.
In 1982, he wrote an article, Epigrams on Programming, for ACM's SIGPLAN journal,
describing in one-sentence distillations many of the things he had learned about
programming over his career. The epigrams have been widely quoted.
He remained at Yale until his death in 1990
1.3.2 Algorithm: In-Order
We will here
As this is a
meaning, all
same can be
develop algorithm to do in-order traversal using right in threaded trees first.
non-recursive method for traversal, it has to be an iterative procedure;
the steps for the traversal of a node have to be under a loop so that the
applied to all the nodes in the tree.
Did you Know?
In-Threaded trees are same as Double Threaded Trees.
The threaded trees which are right-in threaded as well as left-in threaded are known as inthreaded trees.
Institute of Lifelong Learning, University of Delhi
Threaded Trees
1.3.2.2 Developing Algorithm
In-order is L V R that is Go extreme left (pushing all the nodes coming in the way on to
the stack) , visit (print) the node, Go Right. When we go right, there are two possibilities.
1. If right child is there we repeat L V R on that node.
2. If right child is Null then we pop the top most node from the stack (which is
actually one of its not visited parents and if we see it is actually its in-order
successor), Visit it and go right.
The use of stack comes in step 2 of in-order traversal. Here Stacks are omitted by using
threads. Here right pointer either contains a right child link or thread link. These two are
differentiable by using a logical boolean variable as we have earlier seen.
We will be seeing the following right–in threaded tree of Fig 1.8.
Figure 1.8 Right–In Threaded Tree.
Except step 2 of inorder where stacks were used, all the remaining steps will
remain same.
The algorithm thus can be summarized as:
•
•
•
We start at the leftmost node in the tree, print it, and go to right link.
If this is a thread link, we output the node and continue to its right.
If this is a right child link then we go to its leftmost node, print it, and
continue.
Let’s see the step by step execution.
Institute of Lifelong Learning, University of Delhi
Threaded Trees
State Diagram
Step Executed
Output
We start at the left
1
most node and print it
Follow thread to right
and print node
13
Follow link to right , go
to left most node and 1 3 5
print it
Follow thread to right ,
1356
print node
Follow link to right , go
to leftmost node and
13567
print
Follow thread to right
,print
135678
Institute of Lifelong Learning, University of Delhi
Threaded Trees
Follow link to right .go
left most node and
1356789
print,
Follow thread to right
,print node
1 3 5 6 7 8 9 11
Follow link to right, go
to left most node and 1 3 5 6 7 8 9 11 13
print.
Threaded trees can also be used for preorder and post order traversals. Post order is little
more complicated than the logic of preorder traversals. We will discuss this part in next
section on right-in threaded trees. Also right in threaded trees can be used for printing
data in reverse order of in-order traversal i.e. in decreasing order. So we conclude that
with single threaded trees we can do all kinds of traversals with small differences in the
logic of the program.
Other Work
Traversing by maintaining a list of visited nodes
List of visited
nodes:
INORDER:
Institute of Lifelong Learning, University of Delhi
Threaded Trees
step- '6' has a left child i.e
1:
‘3’, which has not
been visited. So, we
put 3 in our "list of
visited nodes" and 3
becomes our current
node in consideration.
3
step- '3' also has a left
2:
child, '1', which is not 31
there in our list of
visited nodes. So, we
put '1' in that list and
make it our current
node in consideration.
step- '1' has no left child,
3:
so we print '1'.Then
we check for its right
child. '1' has no right
child and thus we
check for its threadlink. It has a thread
going till node '3'. So,
we make '3' as our
current
node
in
consideration.
31
1
step- '3' certainly has a left
4:
child but it’s already in
our list of visited
nodes. So, we print
'3'. Then we check for
its right child which is
5 which is not there in
our list of visited
nodes. So we add it to
the list and we make it
our current node in
consideration.
315
13
step- '5' has a no left child,
5:
so we print 5 .Then we
check for its right
child.’5’ has no right
child and thus we
check for its thread
link. It has a thread
going till node ‘6’. So
we make 6 as our
current
node
of
consideration.
315
135
Institute of Lifelong Learning, University of Delhi
Threaded Trees
step- '6' has '3' as the left
6:
child but its already
there in our list of
visited nodes.and its
not there in our list of
visited nodes, so we
print 6 and check its
right child. 6 has a
right child 8 which is
not there in our list of
visited nodes so we
put 8 on our list of
visited node and we
make it our current
node in consideration.
3158
1356
step- '8' has '7' as the left
7:
child which is not in
our list. So we put 7 in
our list and make it
our current node of
consideration.
31587
1356
step- '7' has no left child so
8:
we print 7 and check
for its right child. It
has no right child so
we follow its thread
link to 8.So we make
8 as our current node
of consideration.
31587
13567
step- '8' has '7' as the left
9:
child which is already
in our list of visited
nodes. So we print 8
and follow its right
child link.
31587
135678
And finally… 1 3 5 6 7 8 9
11 13
Algorithm :
Step-1: For the current node check whether it has a left child which is not there in
the visited list. If it has then go to step-2 or else step-3.
Step-2: Put that left child in the list of visited nodes and make it your current node in
consideration. Go to step-6.
Step-3: For the current node check whether it has a right child. If it has then go to
step-4 else go to step-5
Step-4: Make that right child as your current node in consideration. Go to step-6.
Step-5: Check for the threaded node and if it is there, make it your current node.
Step-6: Go to step-1 if all the nodes are not over otherwise quit
Institute of Lifelong Learning, University of Delhi
Threaded Trees
Threaded trees can also be used for preorder and post order traversals. Post order is little
more complicated than the logic of preorder traversals. We will discuss this part in next
section on right-in threaded trees. Also right in threaded trees can be used for printing
data in reverse order of in-order traversal i.e. in decreasing order. So we conclude that
with single threaded trees we can do all kinds of traversals with small differences in the
logic of the program.
1.4 Threaded Trees: Traversals Implementation
After getting a insight into how in-order algorithm works on a right-in threaded tree using
successor threads, let’s move one step forward to write the code for it. We will be using C
programming language as per our course curriculum. The codes given here can easily be
converted to any other language.
1.4.1 Implementation using Right-in Threaded Tree (Successor
Threads)
1.4.1.1 In-Order Implementation
1.4.1.2 Pre-Order Implementation
1.4.1.1 In-Order Implementation
We are considering the following right-in threaded tree with successor threads.
The node for this tree is defined here as a structure named nodetype. Here goes the c
implementation:
Figure 1.9 Right–In Threaded Tree
Algorithm 3: Inorder using Right –In Threaded Trees
struct nodetype
{ int info;
/* Node Type definition for a right in- */
struct nodetype *left;
/* threaded tree */
struct nodetype *right;
boolean rthread;
}
/*1*/ typedef struct nodetype *NODEPTR;
/*2*/
void intrav3(NODEPTR tree)
/*3*/ {
/*4*/
NODEPTR p,q;
/*5*/
P=tree;
/*6*/
do{
/*7*/
q=NULL;
Institute of Lifelong Learning, University of Delhi
Threaded Trees
/*8*/
/*9*/
/*10*/
/*11*/
/*12*/
/*13*/
/*14*/
/*15*/
/*16*/
/*17*/
/*18*/
/*19*/
/*20*/
/*21*/
/*22*/
/*23*/
/*24*/
/*25*/
While(p!=NULL)
{
q=p;
p=p->left;
}
/* end while */
if (q !=NULL)
{
printf (“%d”,q->info);
p=q->right;
while(q->rthread && p!=NULL)
{
printf(“%d\n”p->info);
q=p;
p=p->right;
}
/*end while */
}
/* end if */
}while(q!=NULL);
}
/*end intrav3 */
The code is given in intrav3 () function which is as per the algorithm given in last section.
Let us see in detail how it works on L V R (LEFT VISIT RIGHT) rule of In-order.
1. A pointer to the root node of the threaded tree has to be passed in the formal
parameter to the intrav3 () function.
2. Two temporary pointers of type NODEPTR are created named p and q in the
function on statement 4. In the function, p is always one node ahead of q. To start
with, p is root and q is NULL.
3. The reference to the root node named tree is passed to temp pointer named p for
iteration.
4. The do-while loop of statement 6 to statement 24 executes until q becomes NULL.
q is assigned addresses within the loop.
5. The while loop of statement 8-12 is executing L (go left)part of the in-order which
takes the p pointer to the left most node until it becomes NULL , saving its previous
node’s address in q. so when p becomes NULL then q must be holding the last
nodes address in that left path.
6. Then V (visit or print) part comes which visit the left most node so the value of q is
printed on statement no 15.
7. Then statement 16 executes the R part, which says after visiting a node go right. P
is assigned the value of q’s right pointer node.
8. Now a check is made whether p is reached via a thread link or a child link in
statement 17th while loop. This is done by using the rthread value of q .If rthread
is true for q then p is the successor of q and its value has to be printed and move
right and repeat step 8 again.
9. This inner while executes until we find a child link instead of thread link.
10. if p is child of the q then inner while loop terminates and control jumps to outer
loop again to repeat the L(go left) V (visit the node) R (go right) on this node.
This Function intrav3 terminates when no more nodes left to be traversed. It has used two
pointers p and q and when q becomes null that means all the nodes must have been
traversed. For consistency the last node on the right most paths’ has rthread value set to
true though it has no in-order successor.
The logic is still L V R. i.e. first go left then visit it and go right. If right node is a thread
node then visit it and again go right else if it’s a child node then repeat L V R on it. Repeat
this whole logic until all the nodes are finished.
Institute of Lifelong Learning, University of Delhi
Threaded Trees
Did you Know?
Is space really saved in stackless depth first traversals?
Note that here we are traversing the stack with the help of two variables p and q, and
even the use of q can also be eliminated. No stack is needed and it appears that space is
saved. But is it really so? As indicated nodes require a data member, indicating how the
right pointer is being used. In the above implementation, a logical Boolean data member
rthread plays this role. Hence rthread requires some space and this depends on the
language implementation. If it is optimized then it at least will require 1 byte per node if
not 1 word defeating the argument about saving space by using threads.
1.4.1.2 Pre-Order Implementation
The algorithm here is very simple in fact simpler than the original in-order traversal let’s
see the magic of threads again. The rule behind pre order traversal is V L R i.e. Visit the
node, go left and then go right. Let’s see how it can be accomplished using right-in
threaded trees. Following right-in thread is considered.
Figure 1.10 Right–In Threaded Tree.
We will go to root node, visit it and keep on moving to its left while visiting each node
coming on the way until we reach a Null node. This will accomplish the V L phase of
preorder traversal. When we will reach a null left corner we will move to right of the last
node visited. If this right node is a thread node we will do nothing as it must have been
visited by pre-order visited phase and we will again go to its right. But if we find a child
node on the right instead of a thread node we will repeat V L R on it.
Algorithm 4 : Preorder Traversal using Right in Threaded Trees
struct nodetype
{ int info;
/* Node Type definition for a right in- */
struct nodetype *left;
/* threaded tree */
struct nodetype *right;
boolean rthread;
}
/*1*/ typedef struct nodetype *NODEPTR;
/*2*/
void pretrav(NODEPTR tree)
/*3*/ {
/*4*/
NODEPTR p,q;
/*5*/
P=tree;
/*6*/
do{
/*7*/
q=NULL;
/*8*/
While(p!=NULL)
/*9*/
{
Institute of Lifelong Learning, University of Delhi
Threaded Trees
/*10*/
/*11*/
/*12*/
/*13*/
/*14*/
/*15*/
/*16*/
/*17*/
/*18*/
/*19*/
/*20*/
/*21*/
/*22*/
/*23*/
/*24*/
/*25*/
q=p;
p=p->left;
}
}
/* end while */
if (q !=NULL)
{
printf (“%d”,p->info);
p=q->right;
while(q->rthread && p!=NULL)
{
printf(“%d\n”p->info);
q=p;
p=p->right;
}
/*end while */
}
/* end if */
}while(q!=NULL);
/*end pretrav */
Explanation. 1. Go to root (step 5)
2. Repeat the whole process until no nodes are left.(step 6-20)
3. repeat steps 4 ,5 until p=null .(loop 8-12)
4. visit p , assign p to q and (statement 9,10)
5. Go left of p. (statement 11)
6. go right of q .(statement 14)
7. If this is the thread node then go right again i.e. Go 6.(loop 15 to 19).
8. Else if this is a child node go to 3. (go to do-while loop of statement 6).
Activity
Write code for post-order traversal in a right-in threaded tree.
Post-order traversal is only slightly more complicated. First a dummy node is created that
has the root as its left descendent. In the traversal process, a variable can be used to
check the type of the current action. If the action is left traversal and the current node has
a left descendent, then the descendent is traversed; otherwise the action is changed to
right traversal. If the action is right traversal and the current node has a right non thread
descendent, then the descendent is traversed and the action is changed to left
traversal;otherwise the action changes to visiting a node. If the action is visiting a node,
then the current node is visited and afterward its post-order successor has to be found. If
the current node’s parent is accessible through a thread (that is current node is parent’s
left child), then traversal is said to continue with the right descendent of the parent. If the
current node has no right descendent, then it is the end of the right extended chain of
nodes. First the beginning oof the chain is reached through the thread of the current node,
then the right references of nodes in the chain are reversed, and finally, the chain is
scanned backward, each node is visited, and then right references are restored to the
previous setting.
1.4.2 Implementation using Left-in Threaded Tree(Predecessor
Threads)
We can perform reverse of in-order by using predecessor threads. Let’s see how……
1.4.2.1 Reverse In - Order
We are showing two left –in threaded trees with predecessor threads.
Institute of Lifelong Learning, University of Delhi
Threaded Trees
Figure 1.11 Left–In Threaded Tree1.
Figure 1.12 Left–In Threaded Tree2
The C implementation of reverse in-order traversal is as Algorithm 5 here :
Algorithm 5 : Reverse In-Order Traversal using Predecessor Threads
struct nodetype
{ int info;
/* Node Type definition for a left in- */
struct nodetype *left;
/* threaded tree using predecessor */
struct nodetype *right;
/*threads*/
int lthread;
}
/*1*/ typedef struct nodetype *NODEPTR;
/*2*/
void revintrav(NODEPTR tree)
/*3*/ {
/*4*/
NODEPTR p,q;
/*5*/
P=tree;
/*6*/
do{
/*7*/
/*8*/
/*9*/
/*10*/
/*11*/
/*12*/
/*13*/
/*14*/
/*15*/
/*16*/
/*17*/
q=NULL;
While(p!=NULL)
{
q=p;
p=p->right
}
/* end while */
if (q !=NULL)
{
printf (“%d”,q->info);
p=q->left;
while(q->lthread && p!=NULL)
Institute of Lifelong Learning, University of Delhi
Threaded Trees
/*18*/
/*19*/
/*20*/
/*21*/
/*22*/
/*23*/
/*24*/
/*25*/
{
}
printf(“%d\n”q->info);
q=p;
p=p->left;
}
/*end while */
}
/* end if */
}while(q!=NULL);
/*end revintrav */
Explanation.
Here the logic is just the reverse of in-order traversal using successor threads.
Here we are using predecessor threads i.e. we are using left –in threaded tree.
Using such tree we can produce the reverse of in-order traversal i.e. decreasing
order of the nodes value.
In-order is: L V R (Left Visit Right)
Reverse In-order is: R V L (Right Visit Left)
1. Repeat the whole algorithm until no nodes left. (Loop 6-24)
2. We will go extreme right (R). (Loop 8-12)
3. Then we will print its value (Visit it). (Line 15)
4. Then we will move left (go Left). (Line 16)
5. If the left node is a thread node i.e. its in-order predecessor, we will just visit
it and go to its left again to repeat step 4 on it else go to 5. (Loop 17 -22)
6. If the left node is a left child then we will repeat R V L on this node i.e. go to
step 1. (go line no 6 again and repeat the loop).
The reader is advised to execute the following algorithm on both the trees.
Now in the next section we will be looking at how the threaded trees are created
and its practical use of threaded trees data structures.
FAQs
Threads in linked array representation of the tree.
In the linked array implementation, a thread can be represented by a negative
value of node[p].right. The absolute value of node[p].right is the index in the array
node of the in order successor of node[p]. The sign of node[p].right indicated
whether its absolute value represents a thread (minus) or a pointer to a non empty
sub tree (plus).
A sample node is shown above in which the right field value is –ve which shows that this node has n
right child but has a right in-order successor at index 1 in the node array above below.
#define NUMNODES 500
struct nodetype {
Int info;
int left;
int right;
};
Institute of Lifelong Learning, University of Delhi
Threaded Trees
struct nodetype node[NUMNODES];
The following linked array representation of a sample tree is shown here. Note that the value of right
member variable can be negative (for thread link) and positive (for right child link). The indexes of the
nodes are shown under the nodes in the array.
The above given array can be represented as the following tree,.
Under this implementation, the following routine traverses a right in-threaded binary tree inorder.
Void intrav4(int tree)
{int p , q;
p= tree;
do
{
/*travel down left links keeping q behind p*/
q=0;
While (p!=0){
q=p;
p=node[p].left;
}/*end while*/
if (q!=0) /*check if finished*/
{
printf(“ %d”,node[q].right);
p=node[q].right;
while(p<0){
q= -p;
printf(“%d”,node[q].info);
p=node[q].right;
}
/*end while*/
}
/*end if*/
/*traverse right subtree*/
}while(q!=0);
} /*end intrav4*/
FAQs
Institute of Lifelong Learning, University of Delhi
Threaded Trees
Pre order traversal using Pre Threaded Trees
We may also define right and left pre-threaded trees, in which NULL right and left
pointers of nodes are replaced by their pre-order successor and predecessor
respectively.A right pre-threaded binary tree may also be traversed efficiently in
preorder without the use of stacks.
We will be considering the following right pre threaded tree.
Pre-order with right pre threaded tree.
struct nodetype
{ int info;
/* Node Type definition for a right*/
struct nodetype *left;
/* pre threaded tree using successor
struct nodetype *right;
/* threads pointing to preorder */
boolean rthread;
/*successor */
}
/*1*/
typedef struct nodetype *NODEPTR;
/*2*/
void pretrav(NODEPTR tree)
/*3*/
{
/*4*/
NODEPTR p,q;
/*5*/
p=tree;
/*6*/
do{
/*7*/
q=NULL;
/*8*/
While(p!=NULL)
/*9*/
{
printf(“%d”,p->info);
/*10*/
q=p;
/*11*/
p=p->left;
/*12*/
}
/* end while */
/*13*/
if (q !=NULL)
/*14*/
{
/*15*/
/*16*/
p=q->right;
Institute of Lifelong Learning, University of Delhi
Threaded Trees
/*17*/
/*18*/
/*19*/
/*20*/
}
}
/* end if */
}while(q!=NULL);
/*end prevtrav */
Explanation:
As we can see this is one of the simplest algorithms to traverse the right pre threaded tree in preorde
Here we are not using the boolean variable rthread for doing traversal but it is being maintained f
creating the right pre-threaded tree and maintaining the threads. Once the tree is created and nodes a
inserted, then for traversal this boolean variable is not required.
1.
2.
3.
4.
5.
6.
7.
Go to root (step 5)
Repeat the whole process until no nodes are left.(step 6-19)
repeat steps 4 ,5 until p=null i.e. visit it and traverse the left part(loop 8-12)
visit p , assign p to q and (statement 9,10)
Go left of p. (statement 11)
Go right of q .(statement 16)
If this not null go to 3. i.e. visit it and traverse its left part.
The algorithm is very simple. It starts from the root. It then traverses the left part while visiting ea
node on the way. It then go to tight of the last node visited. It can be a thread node to the preord
successor or the right child. The algorithm gives the same treatment to both as it has to first visit it an
go left if it exists otherwise go right.
1.5 Threaded Trees: Creation and Application
1.5.1 Constructing a Right –In Threaded Binary Tree
1.5.2: Applications of threaded trees
1.5.1 Constructing a Right –In Threaded Binary Tree
We have seen how a right-in threaded tree can be used to for in-order, pre-order and
postorder traversals efficiently without the use of stacks. We will here see how to create a
single right-in threaded tree with successor threads.
The following figure 1.13 shows the creation of a right-in threaded tree as the values
comes one by one. The links in black are child links and the links in red are successor
thread link. The keys keep on coming one by one and they are inserted in the tree first
and then thread links are managed.
See the following figure carefully to get a clear idea of the creation of threaded trees from
scratch.
Figure 1.13Creation of a Right–In Threaded Tree.
Institute of Lifelong Learning, University of Delhi
Threaded Trees
In the above example we have shown the insertion of 6 keys in the threaded tree.
Insertion of a node in threaded tree is no different from insertion in the binary search tree.
Its proper location is found i.e. either in the left sub tree of the toot node or the right sub
tree of the root node and then it is inserted. If a duplicate is found then error is reported.
The only extra step we have to do is maintaining the right successor threads while
inserting the node at its location found. We are presenting the code below.
Now in the next section we will discuss the c implementation of the above threaded tree
creation mechanism.
Interesting Fact
Threading a Binary Tree
If we are given a binary tree, it is natural to ask how threads might be added to it so
that it becomes threaded. Threading a binary tree is an interesting problem, because a
particularly obvious approach is wrong. A first idea might be to find each NULL pointer
and insert the proper thread. However, when you reach NULL pointer you have no way of
determining what the proper thread is. The proper algorithm is based on taking any non
leaf node and setting the threads that should point to it. The algorithm for finding a
node’s predecessor and successor are well defined: the successor of any node Ais the left
–most node in A’s right subtree. The algorithm must traverse the tree level by level,
setting all the threads that should point to each node as it processes the node.
Therefore, each thread is set before the node containing the thread is processed. In fact,
if the node is a leaf node, it need not be processed at all.
The code itself uses the following auxiliary procedures :
PROCEDURE setrightthread(T , SuccT: treeNode)
(* Right(T) is set to succT and isthread(right(T)) is true *)
PROCEDURE setleftthread(T, predT: treeNode)
(* left(T) is set to predT and is thread(left(T)) is true *)
The author uses a queue to traverse the tree level by level. After the threads to the node
being processed are inserted, its children go on the queue (if they exist). During
preprocessing, the thread to the header must be inserted in the tree’s left-most node as
the left thread, and the thread to the header must be inserted in the tree’s right-most
node as the right thread. We now can write the algorithm for threading a binary tree
with header node.
Create (Q)
Enqueue(Q,tree)
Setleftthread(Node(MinElement),Header)
Setrightthread(Node(MaxElement),Header)
While not isempty(Q) do
Dequeue (Q,T)
Setrightthread(pred(T),T)
Setleftthread(succ(T),T)
If not isthread(getleftT)
Then Enqueue(Q,getleftT)
Endif
If not isthread(getrightT)
Then Enqueue(Q,getrightT)
Endif
EndWhile
Institute of Lifelong Learning, University of Delhi
Threaded Trees
1.5.1.1 C Implementation: Threaded Tree Creation
Algoritm 6 : Threaded Tree creation
struct nodetype
{ int info;
struct nodetype *left;
struct nodetype *right;
int rthread;
};
typedef struct nodetype *NODEPTR;
/*1*/main()
/*2*/{
/*3*/
NODEPTR ptree;
/*4*/
NODEPTR p, q;
/*5*/
int number;
/*6*/
scanf (“%d”, &number);
/*7*/
ptree =maketree(number);
/*8*/
while (scanf (%d”, &number) != EOF)
/*9*/
{
/*10*/
p=q=ptree;
/*11*/
while (number !=p->info && q!=NULL)
/*12*/
{ p=q;
/*13*/
if (number < p->info)
/*14*/
q= p->left;
/*15*/
else
q=p->right;
/*16*/
}
/* end while */
/*17*/
if (number== p->info)
/*18*/
printf(“%d is a duplicate “,number);
/*19*/
else if (number< p->info)
/*20*/
setleft(p,number);
/*21*/
else setright(p,number)
/*22*/
} /*end while */
/*23*/ } /*end main */
Explanation.
•
•
•
•
First number is read from the user and a node is created by calling the maketree
function.
This first node is called ptree (parent node of the tree). Thereafter the numbers are
read in a loop (statement 8) until no more numbers.
Then in loop at statement no 11- 16 a location is found in the sub trees of node
ptree where this new number can be inserted.
The loop is exited if a element in the tree is already present with the key value
equal to the number that we want to insert and error is reported otherwise location
p is returned after which the node can be inserted.
Depending upon numbers value either it is inserted to the left of p by calling
setleft() function at statement 20 or to the right of p by calling setright() at
statement 21.
Institute of Lifelong Learning, University of Delhi
Threaded Trees
Biography Sketch
Prof. Joseph Morris
In continuation of our series of biographies of all the
prominent researchers in the field of Data Structures,
we now present you a brief biographic sketch of a very
prominent scientist Prof. Joseph Morris.
Prof. Joseph Morris was educated in Trinity College
Dublin, Ireland, with engineering, and postgraduate
degrees in computing. During his PhD studies he spent
time at the University of Eindhoven in the group of
Prof. Edsger Dijkstra. He has been a software
researcher in U.K., U.S.A., Netherlands, and New
Zealand as well as Ireland, and has worked in industry
in Ireland, Germany, the U.K., and the U.S.A.
He is currently a professor of computing at Dublin City University. His areas of
expertise include concurrent programming, mathematically specifying and verifying
computer programs, and the formal logic that underpins effective reasoning about
programs. His recent research includes a mathematical theory of nondeterministic
behavior in programs which has yielded a way to combine different kinds of
programming styles in a single programming language. He is a founding director of
Lero – the Irish Software Engineering Research Centre, and the Lero Graduate School
in Software Engineering. Joseph Morris has given us Morris in-order algorithm in his
research paper “Joseph Morris. 1980. Traversing binary trees simply and cheaply.
Information Processing Letters, 9, 5, pp197-200.
1.5.1.2 Sub Functions
We now present the sub functions maketree() ,setleft(), and setright() that were used in
the creation of threaded tree above.
/*1*/ NODEPTR maketree(int X)
/*2*/ { NODEPTR p;
/*3*/
p=getnode();
/*4*/
p->info=x;
/*5*/
p->left=NULL;
/*6*/
p->right=NULL;
/*7*/
p->rthread=true;
/*8*/ }
/*-------------------------------------------------------------------------------------------------*/
/*9*/ Void setleft(NODEPTR p, int x)
/*10*/ {
NODEPTR q;
/*11*/
if (p == NULL)
/*12*/
error (“void insertion”);
/*13*/
else if (p->left != NULL)
/*14*/
error (“invalid insertion “);
/*15*/
else {
/*16*/
q=getnode ();
/*17*/
q-> info=x;
/*18*/
p->left=q;
/*19*/
q->left=NULL;
/*20*/
/*the inorder successor of node(q) is node (p) */
Institute of Lifelong Learning, University of Delhi
Threaded Trees
/*21*/
q->right =p;
/*22*/
q->rthread = TRUE;
/*23*/ }
/*end else */
/*24*/ } /* end set left */
/*-----------------------------------------------------------------------------------------------*/
/*25*/ Void setright (NODEPTR p , int x)
/*26*/ {
/*27*/
NODEPTR q,r;
/*28*/
if (p== NULL)
/*29*/
error (“void insertion”);
/*30*/
else if (!p->rthread)
/*31*/
error (“invalid insertion”);
/*32*/ else {
/*33*/
q=getnode();
/*34*/
q->info=x;
/*35*/
/* save the inorder successor of node(p) */
/*36*/
r=p->right;
/*37*/
p->right=q;
/*38*/
p->rthread=FALSE;
/*39*/
q->left=NULL;
/*40*/
/*The inorder successor of node(q) is the */
/*41*/
/*previous successor of node(p)*/
/*42*/
q->right=r;
/*43*/
q->rthread=TRUE;
/*44*/
} /*end else*/
/*44*/ } /* end set right */
Explanation.
1. Function: maketree
maketree takes one parameter of type integer and creates a node by calling a
getnode() function. This node is called p. p’s info, left,right and rthread are set
accordingly.
2. Function: setleft
•
•
•
•
•
This function takes two parameters, a pointer named p to the node on whose left
our new number is to be inserted and a second the number itself.
A new node named q is taken by calling getnode() function again.
The members of q are then assigned value like q’s info part is set to x; its left
pointer is set to NULL. Its right pointer value should hold a thread to p as p will be
its inorder successor.
So q’s right is set equal to p, and hence its rthread value is also set to true.
One last thing needed to be done is setting p’s left equal to q. This can be
visualized in the figure 1.14.
Figure 1.14 Insertion of a node in left.
Institute of Lifelong Learning, University of Delhi
Threaded Trees
3. Function: setright
This function also takes two arguments as parameters. First is a pointer p to the
node on the right of which the new node is to be inserted and the second is the
value of the new node.
We take the case when 13 is inserted in the right of 11.we have shown the tree
before insertion and after the insertion in figure 1.15(a) and 1.15(b)
respectively. As this is a right–in threaded tree right threads of the parent node
needs to managed whose new right child is being inserted. This is clear from the
given figure.
Figure 1.15(a) Before Insertion
Figure 1.15(b) After Insertion
•
•
•
•
First of all a new node q is created by calling the getnode() function.
Before inserting it to the right of q whose reference is already passed as a
parameter to setright() , we save the right thread of p in r.
Then p’s right child is set to q and its rthread value is set to false thus making q as
a right child of p.
Q’s left pointer is set to NULL and its right pointer is set to r and its rthread value is
set to true making r as a successor of q. In the above figure node 13 is q and 11 is
p. Rest all settings are clear in the figure.
The above given functions named maketree() , setleft(), and setright() can also be written
for left-in threaded trees and pre threaded and post threaded trees in the same
manner.after reading the above text , I hope they will appear to be simple.
FAQs
Binary tree traversal using parent/father field vs. Binary tree traversal using
threads.
For comparison we have to see how father fields in every node of a binary tree is
used to traverse the tree in in-order without using stacks. Here comes the code:
struct nodetype
int info;
struct nodetype
struct nodetype
struct nodetype
{
*left;
*right;
*father;
Institute of Lifelong Learning, University of Delhi
Threaded Trees
};
typedef struct nodetype *NODEPTR
Void intrav(NODEPTR tree)
{ NODEPTR p,q;
q=NULL;
p=NULL;
do{
while (p!=NULL)
{
Q=p;
P=p->left;
}
/*end while*/
if (q!=NULL)
{ printf (“%d”,q->info);
P=q->right;
} /*end if*/
while (q!=NULL && p==NULL)
{
do {
/* node (q) has no right son. Back up until a */
/*left son or the tree root is encountered */
p=q;
q=p->father;
} while (!isleft (p) && q!=NULL);
if (q !=NULL){
printf (“%d”,q->info);
p=q->right;
} /*end if */
} /*end while */
}while (q!=NULL);
} /*end intrav
In this function a isleft(p) returns true if p is the left child of its parent otherwise it returns false.In th
inorder traversal a node is visited when its left son is recognized as NULL or when it is reached aft
backing up from its left son. Traversal using father pointers for backing up is less time efficient tha
traversal of a threaded tree. A thread points directly to a node’s successor, whereas a whole seroes
father pointers may have to be followed to reach that successor in an unthreaded tree.
1.5.2 Applications Of Threaded Trees
•
•
Threaded trees are a important data structure where trees are created once with
very less of insertions and deletions operations there on and more of traversal
operations (specifically depth first traversals) are more in number. In such
situations the threaded trees act as a boon for the system performance by reducing
the space required for stacks.
It is also possible to discover the parent of a node from a threaded binary tree,
without explicit use of parent pointers or a stack, albeit slowly. This can be useful
where stack space is limited, or where a stack of parent pointers is unavailable (for
finding the parent pointer via DFS).
This is possible, because if a node (k) has a right child (m) then m's left pointer must be
either a child, or a thread back to k. In the case of a left child, that left child must also
have a left child or a thread back to k, and so we can follow m's left children until we find
a thread, pointing back to k. The situation is similar for when m is the left child of k.
Institute of Lifelong Learning, University of Delhi
Threaded Trees
Frequently asked Questions
Code to discover the parent of a node using Threaded Trees
A sample code is given in Python. Reader is advised to convert the logic given in
any of the programming languages.
In Python:
def parent(node):
if node is node.tree.root:
return None
else:
x = node
y = node
while True:
if is_thread(y.right):
p = y.right
if p is None or p.left is not node:
p=x
while not is_thread(p.left):
p = p.left
p = p.left
return p
elif is_thread(x.left):
p = x.left
if p is None or p.right is not node:
p=y
while not is_thread(p.right):
p = p.right
p = p.right
return p
x = x.left
y = y.right
Activity
A Backup traversal technique
Think of a stack less non recursive backup traversal technique for unthreaded trees,even if
no father field. The technique is simple: simply reverse the son pointers on the way down
the tree so that it can be used to find a way back up. On the way back up pointer is
restored to its original value.
Try to write the logic for above technique.
Institute of Lifelong Learning, University of Delhi
Threaded Trees
Summary
Depth first traversal operations are major operations on trees. We need to traverse the
trees often to get its values. Out of various depth first traversals inorder yields a sorted
output on a binary trees which are sorted in nature. Such methods are written recursively
and iteratively both. Here in this we studied recursive inorder and found that it uses stack
data structure heavily in the background which questions its efficiency on very big trees.
We have then analyzed the iterative counterpart also to make it more efficient..
Non recursive In-order traversal also face the same problem of stack usage as with the
case of recursive in-order. There are so many ways thus found so to overcome this problem
of stack usage in in-order traversals. One of them is threaded trees. Threaded trees are
build by using some unused spaces within the original binary trees. Though insertions and
deletions are quite complex in threaded binary trees, but it is the traversal for which it
known for and used. We have then discussed the implementations of these algorithms.
We have developed an algorithm here to traverse a tree in in-order manner that is
increasing order by key values using right-in threaded trees. We can traverse our tree
maintaining single threads also. We have seen an algorithm here to traverse a tree in
inorder manner that is increasing order by key values using right-in threaded trees which is
similar to the previous versions of in-order algorithms except at one step.
We have seen the implementation of the various traversal algorithms on the threaded
trees. First of all we have done in-order and pre-order traversal on right–in threaded trees.
We have also discussed the logic behind the post –order traversal. Likewise using left –in
threaded trees with predecessor threads, reverse of in-order traversal can be done. We
have also done the implementation for that. Not only with in-threaded trees, we have also
discussed one algorithm for right pre threaded trees to traverse the tree in preorder
efficiently without using stacks. With our discussion it is clear that right–in threaded trees
are enough to do all kinds of traversals. So rather maintaining two threads in a tree only
one thread is sufficient to traverse it any depth first way.
Threaded Trees can be created just as binary search trees are created. The one extra step
which is needed is maintaining the threads after insertion of a node. We have seen in this
chapter how to create a right –in threaded trees. Left-in threaded trees can be created on
the same lines. Using such trees in situations where parent node needs to find is simple.
One such code is also discussed. We now hope that reader will find it easy to deal with
threaded trees and apply it in various applications.
Institute of Lifelong Learning, University of Delhi
Threaded Trees
Exercises
1.1 For the above tree simulate the action of inorder depth first traversal and show
pictorial representation using a stack.
1.2 Count how many push and pop operations are done and report the running time using
this information
1.3 It is said that trees are graphs with no cycle. So can the above cited inorder be made
to execute on a graph in which a node can have as many as three children and no cycle.
Justify.
1.4 In the above question if the given graph contains a cycle then can it be still executed.
If yes then justify. if no then give the modification to the above algorithm.
1.5 Write the non recursive implementation of post order and pre order traversal.
1.6 Which of the above implementations is more difficult? Justify.
1.7 Make a binary search tree of the following numbers. 56, 23,67,78,89,34,12,2,89,23
Now add left threads and right threads to this tree.
1.8 Simulate in-order using right threads and reverse of in-order using left threads and
show each step.
1.9 Develop algorithm for printing the reverse of in-order traversal on a right –in threaded
tree.
1.10 Justify the efficiency of your algorithm?
1.11 Did you require stacks to do get the output along with threads? Justify.
1.12 What are the drawbacks of bi-threaded trees? Are single threaded trees enough to do
traversals on the tree? Justify.
1.13 Write an algorithm for left pre threaded trees to traverse it in ant depth first traversal
algorithm?
Institute of Lifelong Learning, University of Delhi
Threaded Trees
1.14 Discuss the run time and space requirement of the in order traversal algorithm given
above.
1.15 Justify that only single threads are required to traverse the tree efficiently.
1.16 Outline the algorithm for deletion of a node in right-in threaded tree.
1.17 Write functions maketree, setleft, setright for left-in threaded trees, right-pre
threaded trees.
1.18 If a tree is bi threaded for postorder traversal, and then is it enough to traverse it in
in-order and post-order. Justify
Glossary
•
•
•
•
•
•
•
•
•
•
ActivationRecord: These are machine dependent data structures containing
subroutine state Information.
Binary Tree : In computer science, a binary tree is a tree data structure in which
each node has at most two children. Typically the first node is known as the parent
and the child nodes are called left and right.
Binary search tree: Binary Search tree is a binary tree in which the left child of
any node is less than the node and right child of any node is greater than the node.
Depth : The depth of a node n is the length of the path from the root to the
Height : The height of a tree is the length of the path from the root to the
deepest node in the tree. A (rooted) tree with only one node (the root) has a
height of zero (or one).
Queue : Queue ia a First in first out Data structure in which the element which is
inserted in the last is also the last element to be popped.
Run Time Stack : A Run time Stack is a stack data structure that stores
information about the Active subroutines of a computer program.
Recursion : Recursion, in mathematics and computer science, is a method of
defining functions in which the function being defined is applied within its own
definition.
Stack : Stack is a last in fist out data structure in which the element which is
pushed in the last is first to be popped out.
Institute of Lifelong Learning, University of Delhi
Threaded Trees
References
1. Suggested Reading
1. Data structures and algorithm in C++, Adam Drozdek
2. Data Structures using C and C++,Langsam,Augestein and Tanenbaum ,Second
Edition.
3. Abstract data types: specifications, implementations, and applications By Nell Dale,
Henry M. Walke
2. Web Links
1.1
1.2
1.3
1.4
1.5
http://datastructures.itgo.com/trees
http://www.eternallyconfuzzled.com/tuts/datastructures
http://mitpress.mit.edu/sicp/chapter1
http://www.brpreiss.com/books/opus5/html
http://Wikipedia.org
Institute of Lifelong Learning, University of Delhi