* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
Download Threaded Trees Institute of Lifelong Learning, University of Delhi
Survey
Document related concepts
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