Documente Academic
Documente Profesional
Documente Cultură
Lecture No.
14
___________________________________________________________________
Data Structures
Lecture No. 14
Reading Material
Data Structures and Algorithm Analysis in C++
Chapter. 4
4.3, 4.6
Summary
Recursive Calls
Preorder Recursion
Inorder Recursion
Non-Recursive Traversal
Traversal Trace
We discussed the methods of traversal of the binary tree in the previous lecture. These
methods are- preorder, inorder and postorder. It was witnessed that in the C++ code
that the coding of these methods becomes very short with the use of recursive calls.
The whole tree can be traversed with a few lines of code. We also demonstrated the
benefits of the methods with the help of an example in which a tree was traversed by
preorder, inorder and postorder methods. Implementation of the recursion also came
under discussion in the previous lecture.
Recursive Calls
We know that function calls are made with the help of stacks. When a function calls
some other function, the parameters and return address of the function is put in a
stack. The local variables of the function are also located at the stack and the control is
passed to the called function. When the called function starts execution, it performs its
job by using these parameters and local variables. When there in the function there
comes a return statement or when the function ends then the return address, already
stored in the stack, is used and control goes back to the next statement after the calling
function. Similarly when a function is calling to itself, there is no problem regarding the
stack. We know, in recursion a function calls to itself instead of calling some other
function. Thus the recursion is implemented in the way as other function calls are
implemented.
Preorder Recursion
Now lets see the preorder traversal with respect to the recursion. We will see what
changes happen when preorder function calls itself recursively. Consider the following
binary search tree used in our previous example.
Page 1 of 12
9
7
5
18
20
16
17
preorder(14)
14
..preorder(4)
4
....preorder(3)
3
......preorder(NULL)
......preorder(NULL)
....preorder(9)
9
......preorder(7)
7
........preorder(5)
5
..........preorder(NULL)
..........preorder(NULL)
........preorder(NULL)
......preorder(NULL)
For the preorder traversal, we call the preorder function and pass it the root node of
the tree (i.e. 14) as a parameter. We have discussed in the preorder traversing that
preorder method first prints the value of the node that is passed to it. So number 14 is
printed. After it, the preorder method calls itself recursively. Thus we will call the
preorder and give it the left subtree of 14 (actually the root node of left subtree) as an
argument. So this call will be preorder (4). As it is preorder traversing, the value of the
root node i.e. 4 will be printed. Now the preorder method looks if there is a left
subtree of the node (i.e.4). If it has a left subtree, the preorder function will call itself
again. The root node of this left subtree will be passed as an argument to the new
function call. Here it is 3, so the number 3 is printed. Thus by the previous three calls
(i.e. preorder(14), preorder(4) and preorder(3) ) we have printed the values 14, 4 and
3. Now the left subtree of 3 is NULL. We know that in preorder method, first of all we
check whether the node is NULL or not. If it is not NULL, the recursion process
continues. It means that we print the value of the node and call the preorder function
again. If the node is NULL, the process ends.
There is always a terminating condition in recursive call or recursive procedure. In the
previous lecture, while trying to end the factorial recursion, we defined the condition
that the factorial of zero is 1. When the factorial of n starts, the factorial is called again
and again by decreasing the value of n by 1. So when we reach at 0, there is no further
recursive call for the factorial as we have set the condition for it i.e. the factorial of 0 is
equal to 1. Thus the recursion ends. Equally is true about the preorder process. When a
node is NULL, then if statement becomes false and we exit the function (see code of
preorder function in previous lecture).
In the above figure, the preorder process that started from node 14, came to an end at
node 3 as the left subtree of 3 is NULL. Now this preorder process will make no
further recursive call. We will come back from here by using the return address of the
call preorder (NULL) from the stack. If we see the stack, this fourth call will come
back to the third call i.e. preorder (3). The left subtree of 3 has ended. Now we have to
Page 2 of 12
Page 3 of 12
9
7
5
18
20
16
17
..preorder(15)
15
....preorder(NULL)
....preorder(18)
18
......preorder(16)
16
........preorder(NULL)
........preorder(17)
17
..........preorder(NULL
)
..........pre3
der(NULL)
......preord er(20)
20
........preorder(NULL)
........preorder(NULL)
Now we are at node 14 and know that in preorder, the value of the node is printed first
before going to its left subtree. And after traversing the left subtree, a programmer
comes back and goes to traverse the right subtree. As we have traversed the left
subtree of 14 and have come back to 14, now we will traverse the right subtree of 14.
In the right subtree, we reach at node 15. We print the value 15 and look at its left
subtree. The left subtree is NULL so the call to the preorder method is preorder
(NULL). This call makes the condition of if statement false and the recursive calls
ends on this side. Afterwards, we go to the right subtree of 15. The call preorder (18)
is made, followed by printing of the value 18. Then we go to the left subtree of it that
is node 16. After calling preorder(16) and printing it, we go to the left subtree of 16
which is NULL. Now we will move to right subtree of 16 and the call preorder (17)
prints the number 17. After it, the two next calls are preorder (NULL) as the left and
right subtree of 17 are NULL. Thus after traversing the left and right subtree of 16
(which itself is a left subtree of 18), we return to node 18. Then we go to the right
subtree of 18 and reach at node 20. So preorder (20) prints the value 20. There are
again two preorder (NULL) calls as the left and right subtree of 20 are NULL. The
preorder recursive call ends here. Now we go back and reach at node 14 whose left
and right subtrees have been traversed. That means the whole tree having 14 as the
root has traversed.
Inorder Recursion
Now lets see the inorder recursive calls for traversing a tree. It is not different from
the preorder. The pattern of recursive calls will be changed as in the inorder we
traverse the left subtree first before printing the root. Afterwards, the right subtree is
traversed. The following figures (Fig 14.2(a) and 14.2(b)) explains this phenomenon of
inorder recursion by showing the recursive calls.
Page 4 of 12
9
7
5
18
20
16
17
inorder(14)
..inorder(4)
....inorder(3)
......inorder(null)
3
......inorder(null)
4
....inorder(9)
......inorder(7)
........inorder(5)
..........inorder(null)
5
..........inorder(null)
7
........inorder(null)
9
......inorder(null)
14
We start the inorder with the call inorder (14) as the root of the tree is 14. Now in the
inorder traversing, a programmer has to traverse the left subtree before printing the
value of the root node and then going to the right subtree. So the call to the left
subtree of 14 i.e. inorder (4) will lead to the node 4. At the node 4, the same process
of calling its left subtree, before printing its value, will be applied and we reach at node
3 that is the left subtree of 4. From the node 3, we go to its left subtree. This left
subtree is NULL. Here in the inorder method, we have the same terminating condition
as that seen in the preorder i.e. we will not make further recursive calls if there
becomes a NULL node in the method call. We end the recursion at that node and come
back. Now when we come back from the NULL left subtree of 3 this shows that we
have visited the left subtree of 3. So the value of the node i.e. 3 is printed. Now we go
to the right subtree of 3. Here the inorder call is inorder (NULL). This call will make
the if statement, in the inorder method, false, leading to the end of the recursion. Thus
we have visited the whole tree whose root is node 3. So we come back to node 4. As
the left subtree of node 4 has been visited, we print the value of node i.e. 4. Thus we
have printed the numbers 3 and 4. Now we go to the right subtree of 4. The right
subtree of 4 is the node 9. Now we send 9 as an argument to inorder method. So there
is a call inorder (9). As it is inorder traversing, we go to traverse its left subtree before
printing the number 9. We reach the node 7 and make a call inorder (7). Later, we go
to the left subtree of 7 i.e. the node 5. Now we visit the left subtree of 5 which is
NULL. So here the inorder recursion ends and we come back to node 5, print it and go
to the right subtree of 5. The right subtree of 5 is also NULL. So ending recursion
here, we have finally traversed the tree with 5 as its root. After traversing it, we come
back to the node 7. After visiting the left subtree of 7, we print 7 and go to the right
subtree of 7 which is NULL.
After the call inorder (NULL) we have traversed the tree having 7 as the root and it is
Page 5 of 12
14
15
9
7
5
18
20
16
17
..inorder(15)
....inorder(null)
15
....inorder(18)
......inorder(16)
........inorder(null)
16
........inorder(17)
..........inorder(null)
17
..........inorder(null)
18
......inorder(20)
........inorder(null)
20
........inorder(null)
Page 8 of 12
Traversal Trace
Lets compare the recursive inorder and non-recursive inorder and see whether there is
any difference between them. We find no difference between them. Function call takes
place with the help of stack. Explicit stack is also a stack and its behavior is also the
same. Lets compare these. In the left column, we have recursive inorder and on the
Page 9 of 12
nonrecursive inorder
inorder(14)
push(14)
..inorder(4)
..push(4)
....inorder(3)
....push(3)
3
3
4
4
..inorder(9)
..push(9)
....inorder(7)
....push(7)
......inorder(5)
......push(5)
5
5
7
7
9
9
14
14
inorder(15)
push(15)
15
15
inorder(18)
push(18)
..inorder(16)
..push(16)
16
16
..inorder(17)
..push(17)
17
17
18
18
inorder(20)
push(20)
20
20
In recursive inorder ,the function name is inorder(). We are printing the values. In case
of non-recursive, push() method is employed. You can see the order in which tree
nodes are being pushed explicitly on the stack. Then we print the values and use the
pop() method in a special order to see the values of the nodes.
In the recursive inorder we have inorder(14) while in the non-recursive inorder, there
is push(14). In the recursive inorder, we have inorder(4), in the non-recursive inorder
we have push(4). In the recursive inorder we have inorder(3. Whereas in the nonrecursive inorder, you may have push(3). It is pertinent to note that the only difference
in these two cases is that inorder() is in the left column while push() will be found in
the right column. You should not be surprised over this as stack is being used in both
recursive calls and non-recursive calls.
We can use recursive calls in which stack is used internally. However, in case of nonrecursive calls, the stack is used explicitly. Now the question arises which one of these
methods is better. Lets see some other aspects of recursion. When should we use the
recursion with respect to efficiency and implementation? How many statements were
there in the inorder recursive function. The first statement was an if statement which is
the terminating condition. After this, we have only three statements, two calls and one
cout statement. The order in the inorder is inorder(left), cout and then inorder(right).
Thats the complete function. Now see the non recursive function. There are a lot of
statements in this function so that there should be more code in the non-recursive
function. The second thing is the readability. The readability and understanding is better
in the recursive function. One can easily understand what is happening. To understand
the non-recursive code, one has to read the code with care. He has to go through every
statement to see what you are doing. It will also help ascertain if you are doing it right
or not.
Page 10 of 12
Page 11 of 12
14
4
15
9
7
18
16
5
Level-order:
20
17
14 4 15 3 9 18 7 16 20 5 17
Fig 14.4: level order traversal
We started from the root with 14 as a root node. Later, we printed its value. Then we
move to the next level. This is the binary tree so that we may have two elements at
next level i.e. 4 and 15. So we printed 4 and 15 while moving from left to right. Now
we are not moving to left or right subtree of any node. Rather, we move to the next
level. At next level, a programmer will have three nodes that are from left to right as 3,
9 and 18. We printed these values. If we take root node as zero level, it will be at level
2. Now we move to the level 3. Here at level 3, we have 7, 16 and 20. At next level,
we have 5 and 17. So the numbers we are having are- 14 4 15 3 9 18 7 16 20 5
17. How can we programmatically traverse the tree in level order? We will discuss it in
the next lecture.
Page 12 of 12