Chapter 4
Chapter 4
CHAPTER 4
Trees
IV.1. Introduction
A data structure is said to be linear if its elements form a sequence or a linear list. Previous linear
data structures that we have studied like an array, stacks, queues and linked lists organize data in
linear order. A data structure is said to be non-linear if its elements form a hierarchical classification
where data items appear at various levels. Trees and Graphs are widely used non-linear data
structures.
In computer science, a tree is a very general and powerful data structure that resembles a real tree.
It consists of an ordered set of linked nodes in a connected graph, in which each node has at most
one parent node, and zero or more child nodes with a specific order.
In this chapter, in particular, we will explain one of the non-linear data structures known as a tree,
which is easy to maintain on the computer.
➢ Trees
➢ Graphs, etc.
an essential part of many algorithms and data structures. Trees represent a special case of more
general structures known as graphs. Figure 4.1 shows a tree and a non-tree.
A rooted tree is a type of simple tree that contains a special node called the root. All edges in a
rooted tree branch away from this root node, and any node can be selected as the root. An example
of a rooted tree is shown below, where node A is the root:
A A
B C B C
D E F E F
A Tree Not a Tree
Figure 4.1 : A Tree and not a Tree.
B C D
E F G H I J
➢ Node: Each element of a tree is called as node. In the previous figure there are 10 nodes.
➢ Root: The root of a tree is the "top-most" vertex in the tree, from which all other nodes are
directed away from. In the tree above, node A is the root of the tree.
➢ Parent: Parent of a node is the immediate predecessor of a node. In the tree above, C is the
parent of F and G.
➢ Child: Each immediate successor of a node is known as child. In the tree above, B, C, D are
children of A.
➢ Sibling: Two nodes are siblings if they share the same parent. In the tree above, B, C, and
D are siblings, since they share the parent A.
➢ Path: Path is the sequence of consecutive edges from the source node to the destination
node path between A and I is (A,D),(D,I).
➢ Leaf : is a node that has no child node. In the tree above, E, F, G, H, I, and J are leaves.
D E G
B C F
I J K L M
H
P Q R
Declaration
struct GenericNode {
int data;
GenericNode* children[N]; //Maximum number of children
};
typedef GenericNode *GTREE;
Now, we might have a variable of type GTREE called GTree: GTREE GTree;
root node A
left node right node
B C
Right subtree
Left subtree D E F G
Leaf nodes
H I
Figure 4.2 : Binary Tree.
• All the nodes except one node has one and only one child.
• The remaining node has no child.
Also, a skewed binary tree is a binary tree of 𝒏 nodes such that its depth is (𝒏 − 𝟏).
A A
B B
C C
Level = 0 A
Height = 3 A
Height = 3
Level = 1 B C B
C
Level = 2 D E F J
Level = 3 D
I G K L M N P R
If you are given a generic tree 𝑻 , where each node can have more than two children, you can turn
this generic tree into a binary tree 𝑻′ using the following procedure (starting from the root):
Example : A
A A
B B
C E C
D F D
3) Node E has no children, so we do not have to do any additional work for E. Node F, however,
has three children: I, J, and K. We will set node I as the left child of node F, and set nodes J
and K as a chain of right children of node I.
4) Node C has two children: G and H. We will set G as the left child of C, and H as the right
child of G:
A A
B B
E C E C
F D F G
D
I
I
J H
J
K
K
➢ Elements start being stored from position 𝟏, with position 𝟎 remaining vacant.
➢ The root of the binary tree is placed at index 𝟏 of the array.
➢ The left child of the node at index 𝒊 should be placed at index 𝟐𝒊. If node 𝒊 has no left child,
then index 𝟐𝒊 should be empty.
➢ The right child of the node at index 𝒊 should be placed at index 𝟐𝒊 + 𝟏. If node 𝒊 has no
right child, then index 𝟐𝒊 + 𝟏 should be empty.
➢ The empty positions in the tree where no node is connected are represented in the array
using −𝟏, indicating the absence of a node.
A
Example 1:
B
A B C D E F G H I C
[0] [1] [2] [3] [4] [5] [6] [7] [8] [9]
E F G
However, binary trees need not be complete, so we D
cannot avoid potential gaps in our underlying array.
H I
Example 2: A
For example, if we implemented the following binary tree
using an array, indices 𝟓, 𝟔, and 𝟖 would be empty: B
C
A B C D E F
D E
[0] [1] [2] [3] [4] [5] [6] [7] [8] [9]
Because indices in the underlying array may be skipped, an
array-based approach can be space-prohibitive for sparse F A
trees (as memory would be wasted by allocating space for
nodes that do not exist). B
Example 3:
C
In the worst case, we could get a stick like this, which would require us to
allocate a giant array that remains mostly empty: D
A B C F D
[0] [1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11] [12] [13] [14] [15]
Space Complexity
The worst case occurs when we have a stick, as shown in the example above. When this happens,
only a single node can exist at each depth of the tree. This is because the maximum number of
nodes that can exist at any depth 𝒅 is 𝟐𝐝−𝟏 , assuming the root has a depth of 𝟏. Thus, the number
of array positions needed to support that depth 𝒅 is 𝟐𝐝−𝟏 .
If we have a stick, the depth of our tree would be 𝒏, and the array size we need to support a depth
of d is: 𝟐𝟎 + 𝟐𝟏 + 𝟐𝟐 + ⋯ + 𝟐𝒏−𝟐 + 𝟐𝒏−𝟏 = 𝟐𝒏 − 𝟏 = 𝓞(𝟐𝐧 )
B C
D E F
int data;
BTNode *left;
BTNode *right;
};
Now, we might have a variable of type BTREE called BTree: BTREE BTree;
Using this approach, the time complexity of finding a node’s parent would be 𝓞(𝟏) instead of
𝓞(𝒏), since we have direct access to the parent.
struct BTNode {
int data;
BTNode *parent;
BTNode *left;
BTNode *right;
};
However, this implementation is rarely used because it is memory intensive, and the benefit of a
parent isn’t often worth the extra memory. Usually, we do not need a parent pointer, since the
behavior of a parent pointer can be emulated using recursion (i.e., when a recursive call unrolls, we
automatically move from a child to its parent; there’s no need to store an explicit pointer).
A. Preorder Traversal
To conduct a preorder traversal, you would complete the following steps, in this order:
1) process the parent node (the node the recursion is currently on)
2) recursively process the left subtree
3) recursively process the right subtree
The code for a preorder traversal is shown below:
void preorder(BTREE BTree) {
if (!BTree) return;
} // preorder()
Note that the process() function in the code above is just a placeholder for the work that is done
on each node.
Example:
For example, to print out the nodes in a preorder traversal, we use a print statement in process()
or replace process() with a print statement:
void process(int data) { 7
cout << data << endl;
} 4
2
Exercise :
1 6
What is the preorder traversal of the following tree? 9
To illustrate this process, we will label each node with three letters "𝑷 , 𝑳, and 𝑹" that represent
each of these three steps.
➢ We will mark each letter as "completed" as soon as we finish each step (i.e., we will mark the
𝑷 step of a node as completed after we finish processing its value, we will mark the 𝑳 step of
a node as completed as soon as we finish processing its left subtree, and we will mark the 𝑹
step of a node as completed as soon as we finish processing its right subtree).
➢ In a preorder traversal, 𝑷 must be completed before 𝑳, and 𝑳 must be completed before 𝑹.
P L R 7
P L R 4 2 P L R
P L R 9 1 P L R 6 P L R
P L R 8 P L R 5 3 P L R
We start at the root (𝟕), which is our current stack frame. The first step is to process the data of
the current node, so the first element in our preorder traversal is 𝟕. In fact, the first element of a
preorder traversal is always the root, since a node is always processed before its children.
P L R 7
Preorder Traversal :
P L R 4 P L R
7
2
1 P L R 6 P L R
P L R 9
7
8 5 3
Stack frames over time
P L R P L R P L R
After processing the value of the root node, we mark its 𝑷 step as complete. The next step is to
recursively process the left child (𝑳), so we make a recursive call on node 𝟒, which becomes the
value of our current stack frame.
P L R 7
Preorder Traversal :
P L R 4 P L R
7
2
1 P L R 6 P L R
P L R 9 4
7 7
8 5 3
Stack frames over time
P L R P L R P L R
First, we will process the value of our current node, so 𝟒 is the next value in our preorder traversal.
We then mark the 𝑷 step of node 𝟒 as completed and make a recursive call on the left child of 𝟒
(node 𝟗), which becomes the value of our current stack frame.
P L R 7
Preorder Traversal :
P L R 4 P L R
7, 4
2
1 P L R 6 P L R
P L R 9 4
7 7
8 5 3
Stack frames over time
P L R P L R P L R
P L R 7
Preorder Traversal :
P L R 4 P L R
7, 4
2
9
1 P L R 6 P L R
P L R 9 4 4
7 7 7
8 5 3
Stack frames over time
P L R P L R P L R
We process the value of our current node, so 𝟗 is next in our preorder traversal. Since 𝟗 has no
children, the recursive calls on its left and right children can be completed trivially, and we can
mark the 𝑳 and 𝑹 steps of node 𝟗 as complete. Since all the work for node 𝟗 is finished, the
recursive call unrolls, and we return to the stack frame of node 𝟒. The 𝑳 step of node 𝟒 is now
complete.
P L R 7
Preorder Traversal :
P L R 4 P L R
7, 4, 9
2
9
1 P L R 6 P L R
P L R 9 4 4
7 7 7
8 5 3
Stack frames over time
P L R P L R P L R
P L R 7
Preorder Traversal :
P L R 4 P L R
7, 4, 9
2
9
1 P L R 6 P L R
P L R 9 4 4 4
7 7 7 7
8 5 3
Stack frames over time
P L R P L R P L R
Since the recursive call on the left child is done, the next step is to make a recursive call on the
right child of node 𝟒, which is node 𝟏. Node 𝟏 thus becomes the value of our current stack frame.
P L R 7
Preorder Traversal :
P L R 4 P L R
7, 4, 9
2
9 1
1 P L R 6 P L R
P L R 9 4 4 4 4
7 7 7 7 7
8 5 3
Stack frames over time
P L R P L R P L R
We first process the value of our current node, so 𝟏 is next in our preorder traversal. We then make
a recursive call on 𝟏’s left child, or node 𝟖. Node 𝟖 then becomes the node on our current stack
frame.
P L R 7
Preorder Traversal :
P L R 4 P L R
7, 4, 9,1
2
9 1
1 P L R 6 P L R
P L R 9 4 4 4 4
7 7 7 7 7
8 5 3
Stack frames over time
P L R P L R P L R
P L R 7
Preorder Traversal :
P L R 4 P L R
7, 4, 9,1
2
8
9 1 1
1 P L R 6 P L R
P L R 9 4 4 4 4 4
7 7 7 7 7 7
8 5 3
Stack frames over time
P L R P L R P L R
The value of node 𝟖 is processed first, so 𝟖 is next in our preorder traversal. Since node 𝟖 has no
left or right children, we do not need to do any more work for this node. The recursion unrolls,
and we return to the stack frame of node 𝟏.
P L R 7
Preorder Traversal :
P L R 4 P L R
7, 4, 9,1, 8
2
8
9 1 1 1
1 P L R 6 P L R
P L R 9 4 4 4 4 4 4
7 7 7 7 7 7 7
8 5 3
Stack frames over time
P L R P L R P L R
Now, we will make a recursive call on the right child of node 𝟏. However, node 𝟏 has no right
child, so this step can be done trivially. All three steps for node 𝟏 are now complete, so the
recursion unrolls, and we return to the stack frame of node 𝟒.
P L R 7
Preorder Traversal :
P L R 4 P L R
7, 4, 9,1, 8
2
8
9 1 1
1 P L R 6 P L R
P L R 9 4 4 4 4 4 4
7 7 7 7 7 7 7
8 5 3
Stack frames over time
P L R P L R P L R
All three steps for node 𝟒 are done, so the recursion unrolls, and we return to the stack frame of
node 𝟕. Since the entire left subtree of node 𝟕 has been completely processed, we can mark the 𝑳
step of the root node as complete.
P L R 7
Preorder Traversal :
P L R 4 P L R
7, 4, 9,1, 8
2
8
9 1 1
1 P L R 6 P L R
P L R 9 4 4 4 4 4 4
7 7 7 7 7 7 7 7
8 5 3
Stack frames over time
P L R P L R P L R
Our next step is to make a recursive call on the right child of 𝟕, or node 𝟐.
P L R 7
Preorder Traversal :
P L R 4 P L R
7, 4, 9,1, 8
2
8
9 1 1
1 P L R 6 P L R
P L R 9 4 4 4 4 4 4 2
7 7 7 7 7 7 7 7 7
8 5 3
Stack frames over time
P L R P L R P L R
We then process the value of node 𝟐, so 𝟐 is next in our preorder traversal. Since 𝟐 has no left
child, the recursive call on its left child can be completed trivially. We then make a recursive call
on the right child of 𝟐, or node 𝟔. Node 𝟔 now becomes the node on our current stack frame.
P L R 7
Preorder Traversal :
P L R 4 P L R
7, 4, 9,1, 8, 2
2
8
9 1 1 6
1 P L R 6 P L R
P L R 9 4 4 4 4 4 4 2 2
7 7 7 7 7 7 7 7 7 7
8 5 3
Stack frames over time
P L R P L R P L R
We now process the value of node 𝟔 and add it to our preorder traversal. Then, we make a recursive
call on the left child of node 𝟔, or node 𝟓. Node 𝟓 now becomes the node on our current stack
frame.
P L R 7
Preorder Traversal :
P L R 4 P L R
7, 4, 9,1, 8, 2, 6
2
8 5
9 1 1 6
1 P L R 6 P L R
P L R 9 4 4 4 4 4 4 2 2
7 7 7 7 7 7 7 7 7 7
8 5 3
Stack frames over time
P L R P L R P L R
We process the value of node 𝟓 by adding it to our preorder traversal. Since 𝟓 has no left or right
children, no more work needs to be done on this node. The recursion unrolls, and we return to the
stack frame of node 𝟔.
P L R 7
Preorder Traversal :
P L R 4 P L R
7, 4, 9,1, 8, 2, 6,5
2
8 5
9 1 1 6
1 P L R 6 P L R 4 4 4 4 4 4 2 2
P L R 9 6
7 7 7 7 7 7 7 7 7 7 2
7
P L R 8 5 3 Stack frames over time
P L R P L R
P L R 7
Preorder Traversal :
P L R 4 P L R
7, 4, 9,1, 8, 2, 6,5
2
8 5
9 1 1 6
1 P L R 6 P L R 4 4 4 4 4 4 2 2
P L R 9
7 7 7 7 7 7 7 7 7 7
Now, we will make a recursive call on the right child of node 𝟔, or node 𝟑.
P L R 7
Preorder Traversal :
P L R 4 P L R
7, 4, 9,1, 8, 2, 6,5
2
8 3
9 1 1 6 6
1 P L R 6 P L R 4 4 4 4 4 4 2 2 2
P L R 9
7 7 7 7 7 7 7 7 7 7 7
We process the value of node 𝟑 by adding it to our preorder traversal. Since 𝟑 has no left or right
children, no more work needs to be done on this node. The recursion unrolls, and we return to the
stack frame of node 𝟔.
P L R 7
Preorder Traversal :
P L R 4 P L R
7, 4, 9,1, 8, 2, 6,5, 3
2
8 3
9 1 1 6 6
1 P L R 6 P L R 4 4 4 4 4 4 2 2 2
P L R 9
7 7 7 7 7 7 7 7 7 7 7
P L R 7
Preorder Traversal :
P L R 4 P L R
7, 4, 9,1, 8, 2, 6,5, 3
2
8 3
9 1 1 6 6 6
1 P L R 6 P L R 4 4 4 4 4 4 2 2 2 2
P L R 9
7 7 7 7 7 7 7 7 7 7 7 7
P L R 7
Preorder Traversal :
P L R 4 P L R
7, 4, 9,1, 8, 2, 6,5, 3
2
8 3
9 1 1 6 6 6
1 P L R 6 P L R 4 4 4 4 4 4 2 2 2 2 2
P L R 9
7 7 7 7 7 7 7 7 7 7 7 7 7
All three steps for node 𝟐 have also been completed, so the recursion unrolls, and we return to the
stack frame of node 𝟕.
P L R 7
Preorder Traversal :
P L R 4 P L R
7, 4, 9,1, 8, 2, 6,5, 3
2
8 3
9 1 1 6 6 6
1 P L R 6 P L R 4 4 4 4 4 4 2 2 2 2
P L R 9 2
7 7 7 7 7 7 7 7 7 7 7 7 7 7
The function returns, and the traversal is complete. The preorder traversal of this tree is:
𝟕, 𝟒, 𝟗, 𝟏, 𝟖, 𝟐, 𝟔, 𝟓, 𝟑.
Uses of Postorder Traversal:
B. Inorder Traversal
To conduct an inorder traversal, you would complete the following steps, in this order:
1) recursively process the left subtree
2) process the parent node (the node the recursion is currently on)
3) recursively process the right subtree
The code for an inorder traversal is shown below:
void inorder(BTREE BTree) {
if (!BTree)
return;
} // inorder()
In the previous exercise, we used the same method, the inorder traversal of this tree is:
𝟗, 𝟒, 𝟖, 𝟏, 𝟕, 𝟐, 𝟓, 𝟔, 𝟑.
Uses of Inorder Traversal:
➢ In the case of binary search trees (BST), Inorder traversal gives nodes in non-decreasing
order.
C. Postorder Traversal
To conduct a postorder traversal, you would complete the following steps, in this order:
1) recursively process the left subtree
2) recursively process the right subtree
3) process the parent node (the node the recursion is currently on)
The code for a postorder traversal is shown below:
void postorder(BTREE BTree) {
if (!root)
return;
} // postorder()
In the previous exercise, we used the same method, the postorder traversal of the tree is
𝟗, 𝟖, 𝟏, 𝟒, 𝟓, 𝟑, 𝟔, 𝟐, 𝟕.
Uses of Preorder Traversal:
D. Level-Order Traversal
The level-order traversal is another traversal method that can be used to process the nodes of a
tree. In a level-order traversal, nodes are processed in order of increasing depth, where nodes on
the same level (i.e., nodes that have the same depth) are processed from left to right.
In the previous exercise, the level-order traversal processes nodes in order of increasing depth,
from left to right: 7, 4, 2, 9, 1, 6, 8, 5, 3.
Unlike the other three traversal methods, the level-order traversal is iterative rather than recursive.
To conduct a level-order traversal, a queue is used in place of recursive calls. The code for a level-
order traversal is shown below:
void level_order(BTREE BTree) {
if (!BTree)
return;
if (current->left) {
EnQueue(bfs, current->left);
if (current->right) {
} // while
} // level_order()
In the code, nodes are pushed into the queue in level order (i.e., nodes closer to the root are pushed
in before nodes closer to the leaves). Since queues support first-in, first-out (FIFO) behavior, we
are able to process the nodes in the same order that they are pushed into the queue. Consider the
tree in the previous example:
First, we push (EnQueue) the root into the queue, as shown on line 5: Level-order traversal:
Then, as long as the queue is not empty, we would repeat the following: Queue : bfs
1) Take out the node at the front (by peek) of the queue. 7
2) Process the node (in this case, print it to the level-order traversal).
3) Push (EnQueue) the node’s left child into the queue, if one exists. Level-order traversal:
4) Push (EnQueue) the node’s right child into the queue, if one exists. 7
Queue : bfs
During the first iteration of the while loop, we take out the node at the 4 2
front of the queue (node 7), add it to our level-order traversal, and push
7’s children (nodes 4 and 2) into the queue. Level-order traversal:
During the second iteration, we take out the node at the front of the 7, 4
queue (node 4), add it to our level-order traversal, and push 4’s children Queue : bfs
(nodes 9 and 1) into the queue. 2 9 1
During the third iteration, we take out node 2, add it to our level-order Level-order traversal:
traversal, and push 2’s child (node 6) into the queue. 7, 4, 2
Queue : bfs
During the fourth iteration, we take out node 9 and add it to our level-
9 1 6
order traversal. Since 9 has no children, nothing gets pushed into the
queue. Level-order traversal:
During the fifth iteration, we take out node 1, add it to our level-order 7, 4, 2, 9
traversal, and push 1’s child (node 8) into the queue. Queue : bfs
1 6
Level-order traversal: 7, 4, 2, 9, 1
Queue : bfs
7, 4, 2, 9, 1, 6
6 8
Queue : bfs
8 5 3
Level-order traversal:
During the seventh iteration, we take out node 8 and add it to our 7, 4, 2, 9, 1, 6, 8
level-order traversal. 8 has no children, so nothing gets pushed into the Queue : bfs
queue. 5 3
During the eighth iteration, we take out node 5 and add it to our Level-order traversal:
level-order traversal. 5 has no children, so nothing gets pushed into the 7, 4, 2, 9, 1, 6, 8, 5
queue. Queue : bfs
During the ninth iteration, we take out node 3 and add it to our level- 3
order traversal. 3 has no children, so nothing gets pushed into the
queue. After this iteration, the queue is empty, and our level-order Level-order traversal:
traversal is complete. 7, 4, 2, 9, 1, 6, 8, 5, 3
Queue : bfs
Example :
Traverse the following binary tree in pre, post, inorder and level order.
P
Preorder traversal yields:
P, F , B, H, G , S, R, Y, T, W, Z
F
S
Inorder traversal yields:
B, F , G , H, P, R, S, T, W, Y, Z
B H R Y
Postorder traversal yields:
B, G , H, F , R, W, T, Z, Y, S, P
G T Z
Level order traversal yields:
P, F , S, B, H, R, Y, G , T, Z, W
W
IV.5.2.6. Binary Search Trees (BST)
One pitfall of the standard binary tree is that its elements are not ordered in any manner. This can
make searching difficult: if you wanted to find a given element, that element could be anywhere in
the tree! To address this issue, we will introduce the binary search tree (BST), which is a binary
tree whose elements are ordered based on a sorting invariant.
A special type of binary trees, called binary search tree is a binary tree in symmetric order. Each
node has a key, and for each node in the tree the value of any node in its left subtree is less than
the value of the node and the value of any node in its right subtree is greater than the value of
the node.
A standard non-empty binary search tree exhibits the following properties:
10 10 10
4 7 4 15
15 15
3 7 13 3 4 13 3 11 13 18
18 18
3 5 8 5
Since keys in a binary search tree are ordered in a way such that all keys to the left are smaller and
all keys to the right are larger, the inorder traversal of a binary search tree will always visit its keys
in sorted order.
➢ If you want to search for a target key 𝒌, and 𝒌 is smaller than the current root element you
are looking at, then 𝒌 must be in the left subtree if it exists.
➢ Otherwise, if 𝒌 is larger than the current root element, then k must be in the right subtree
if it exists.
You can think of this as a "binary search" of the tree’s elements since you are removing half of the
search space every time; hence why this data structure is called a binary search tree!
The search algorithm can be implemented both iteratively and recursively. The following iterative
implementation returns a pointer to the node with key 𝒌 if it exists; otherwise, it returns NULL
(nullptr).
BTNode* search_BST(BTREE BTree, int k) {
else {
return BTree;
}
The following implementation does the same thing, but with recursion instead of iteration. Note
that this implementation is tail recursive, since the recursive call is always done last.
BTNode* search_BST(BTREE BTree, int k) {
if (BTree == NULL || BTree -> data == k) {
return BTree;
}
if (k < BTree -> data) {
return search_BST(BTree -> left, k);
}
return search_BST(BTree -> right, k);
}
What is the time complexity of searching for a key in a binary search tree? In the worst case, the
binary search tree may take the form of a stick, and you may have to search the entire tree to find
a given key. This results in 𝓞(𝒏), runtime, where 𝒏 is the number of nodes in the tree.
The Maximum and the Minimum
➢ To find the minimum identify the leftmost node, i.e. the farthest node you can reach by
following only left branches.
} }
} }
} }
if (BTree == NULL) {
BTree = Create_BTNode(k);
else
else {
Similar to search, the time complexity of insert depends on the height of the tree. In the worst case,
the binary search tree could be in the form of a stick, requiring the algorithm to traverse the entire
tree before finding the position to insert the new node — this would take 𝓞(𝒏) time.
53 53
26 26 85
85
remove 88
19 34 70 19 34 70
88
12 47 71 12 47 71
53 53
26 26 85
85
remove 34 19 47
19 34 70 70
12 47 71 12 71
26 85 26 85
19 47
remove 19
70 12 47 70
12 71 71
53 47
70
26 85 26
26 85 85
remove 53
12 47 70 12
12 47 71 70
71
Replace 53 with 70, Replace 53 with 47,
then delete original 70 then delete original 47 71
The smallest node in the right subtree of a given node 𝒌 is known as the inorder successor of node
𝒌. Similarly, the largest node in the left subtree of a given node 𝒌 is known as the inorder
predecessor of node 𝒌. This is because the inorder successor of 𝒌 comes directly after (i.e.,
succeeds) 𝒌 in an inorder traversal, and the inorder predecessor of 𝒌 comes directly before (i.e.,
precedes) 𝒌 in an inorder traversal.
Inorder traversal: 12, 26, 47, 53, 70, 71, 85
BTNode* inorder_successor;
if (BTree == NULL) {
return;
else {
delete node_to_delete;
delete node_to_delete;
} // while
} // else
} // tree_remove()
Replacing a deleted node with either its inorder successor or predecessor is the simplest way to
approach deletion, as it maintains the BST property without requiring you to reconfigure the other
elements in the tree.
The process for finding the inorder successor or predecessor of any node is also relatively
straightforward. To identify the inorder successor of a node, go right once, then go as far left as
possible until you reach a node without a left child. To identify the inorder predecessor of a node,
go left once, then go as far right as possible until you reach a node without a right child.