Binary
Binary
2 BST Operations
Although this traversal is valid, it is not very useful. Let’s try a postorder
traversal and see if it is more useful.
12 20 18 35 52 44 23
Again, this sequence holds little promise of being useful. Let’s try an
inorder traversal.
12 18 20 23 35 44 52
This traversal has some very practical use: the inorder traversal of a
binary search tree produces a sequenced list. What happens if you traverse
the tree using a right-node-left sequence? Try it and see.1
Searches
In this section we study three search algorithms: find the smallest node, find
the largest node, and find a requested node (BST search).
Algorithm 7-1 Analysis As is typical with trees, this algorithm is recursive. The first call starts with the root of the
tree, as shown in Figure 7-5. We then follow a path down the left subtrees. If the left
1. As you can see, the right-node-left traversal traverses the tree in a descending sequence. This is not a
standard traversal, but it can be very useful.
Chapter 7 Binary Search Trees 303
subtree is not null, we must keep looking farther to the left. We do so with a recursive
call to findSmallestBST with the left subtree. The base case in this algorithm
occurs when we find an empty left subtree. At this point we return the address of the
current node, which is the node containing 12 as the base case. Because the recursive
call is part of a return statement, as we move back up the tree we continue to return the
node address to the smallest node until we finally return it to the initiating module.
Left Subtree
not Empty
Left Subtree 23
not Empty
18 ~
Left Subtree
Empty—Return
12 ~
BST Search
We now examine the most important feature of binary search trees, the binary
tree search, which locates a specific node in the tree. To help us understand how
the BST search works, let’s revisit the binary search algorithm, as shown in
Figure 7-6. This figure traces each of the possible search paths from the mid-
dle element in the array. Starting with 23, the binary search examines either
18 or 44, depending on the search key. From 18 it examines either 12 or 20;
from 44 it examines either 35 or 52. As is clear in the figure, tracing all possi-
ble search paths follows the same paths we see in a binary search tree.
304 Section 7.2 BST Operations
Sequenced array
12 18 20 23 35 44 52
23
18 44
12 20 35 52
Now let’s reverse the process. Find a given node in a binary search tree.
Assume we are looking for node 20. We begin by comparing the search argu-
ment, 20, with the value in the tree root. Because 20 is less than the root
value, 23, and because we know that all values less than the root lie in its left
subtree, we go left. We now compare the search argument with the value in
the subtree, 18. This time the search argument is greater than the root value,
18. Because we know that values greater than the tree root must lie in its
right subtree, we go right and find our desired value. This logic is shown in
Algorithm 7-3.
Algorithm 7-3 Analysis We implement the BST search using recursion. In this algorithm there are two base
cases: either we find the search argument in the tree, in which case we return the
Chapter 7 Binary Search Trees 305
address of its node (statement 5.1), or the search argument doesn’t exist, in which
case we return null (statement 1.1).
Study the returns at statements 3.1 and 4.1 carefully. Note that they are returning the
value given by the recursive call, which as we saw earlier is either null or the address of
the node we are trying to locate. These statements are necessary to pass the located
address back through the recursion to the original requester. Figure 7-7 traces the path
through the binary search tree from Figure 7-4, as we search for node 20 using
Algorithm 7-3.
Target: 20
1 if (empty tree)
to 20
1 return null
2 end if
3 if (targetKey < root)
1 return searchBST (left subtree, …)
4 elseif (targetKey > root) target < root
1 return searchBST (right subtree, …) go left
5 else
1 return root 23
6 end if
to 20
1 if (empty tree)
~
1 return null target > root
2 end if go right
3 if (targetKey < root)
1 return searchBST (left subtree, …) 18
4 elseif (targetKey > root)
1 return searchBST (right subtree, …)
5 else
1 return root
6 end if Return pointer to 20 target = root
return root
1 if (empty tree)
1 return null
2 end if
~ 20
3 if (targetKey < root)
1 return searchBST (left subtree, …)
4 elseif (targetKey > root)
1 return searchBST (right subtree, …)
5 else
1 return root
6 end if
Insertion
The insert node function adds data to a BST. To insert data all we need to do is
follow the branches to an empty subtree and then insert the new node. In
other words, all inserts take place at a leaf or at a leaflike node—a node that
has only one null subtree.
Figure 7-8 shows our binary search tree after we have inserted two
nodes. We first added node 19. To locate its insertion point, we searched the
tree through the path 23, 18, and 20 to a null left branch. After locating the
insertion point, we inserted the new node as the left subtree of 20. We then
added 38. This time we searched the tree through 23, 44, and 35 to a null
right subtree and inserted the new node.
306 Section 7.2 BST Operations
23 23
18 44 18 44
12 20 35 52 12 20 35 52
19
23 23
18 44 18 44
12 20 35 52 12 20 35 52
19 19 38
Algorithm 7-4 Analysis The algorithm is quite elegant, but it is not easy to see how the new node is inserted at
the correct location. To help, let’s insert a node into a tree as shown in Figure 7-9.
9 9
23 23
12 20 12 20
Subtree empty
9 Insert here 9 19
To insert 19 into the tree, we start with root. Because the new node’s key is less than
the root key, we recursively call addBST using the left subtree (12). The new node’s
key is now greater than the root key, so we again call recursively with the right subtree,
which is null. At this point we discover that the subtree is null, so we insert the new
node, replacing the null subtree as 12’s right subtree.
Deletion
To delete a node from a binary search tree, we must first locate it. There are four
possible cases when we delete a node:
1. The node to be deleted has no children. In this case, all we need to do is
delete the node.
2. The node to be deleted has only a right subtree. We delete the node and
attach the right subtree to the deleted node’s parent.
3. The node to be deleted has only a left subtree. We delete the node and
attach the left subtree to the deleted node’s parent.
4. The node to be deleted has two subtrees. It is possible to delete a node
from the middle of a tree, but the result tends to create very unbalanced
308 Section 7.2 BST Operations
trees. Rather than simply delete the node, therefore, we try to maintain the
existing structure as much as possible by finding data to take the place of
the deleted data. This can be done in one of two ways: (1) we can find the
largest node in the deleted node’s left subtree and move its data to replace
the deleted node’s data or (2) we can find the smallest node on the deleted
node’s right subtree and move its data to replace the deleted node’s data.
Regardless of which logic we use, we will be moving data from a leaf or a
leaflike node that can then be deleted. Prove to yourself that either of
these moves preserves the integrity of the binary search tree.
The pseudocode for the binary search tree delete is shown in Algorithm 7-5.
Algorithm 7-5 Analysis You need to study this algorithm carefully to fully understand it. First, note that it is a
recursive algorithm. There are two base cases: First, we do not find the node. In that
Chapter 7 Binary Search Trees 309
case the root pointer is null. This case is handled in statement 1.1. The second case
occurs after we have deleted the node, at either statement 5.1.2 or statement 5.2.2.
The first two cases on page 313 have been combined in one case in the actual
implementation of the algorithm. If the left subtree is null, we can simply connect the right
subtree to the parent (root). If the right subtree is null, we are connecting a null subtree,
which is correct. If the right subtree is not null, its data are connected to the deleted
node’s parent, which is Case 2. On the other hand, if the left subtree is not null, we test
to see whether the right subtree is null. If the right subtree is null, we can move the left sub-
tree pointer to its parent.
The most difficult logic in this algorithm occurs when the node is not a leaf. You need to
study this situation carefully to fully understand it. We begin by searching for the largest
node on the left subtree and move its data to replace the data to be deleted. We then call
the algorithm recursively, giving it a new delete target, the key of the leaf node that contains
the data we moved to the internal or root node. This guarantees that when we find the tar-
get key this time, at least one of its subtrees is null. We trace this logic in Figure 7-10.
23 17 Node to be 17
deleted
dltKey
9 23 9 23
5 11 21 17 5 11 21 17
20 22 20 22
Largest key on
left subtree
17 Move largest 17
data here
9 22 9 22
5 11 21 17 5 11 21 17
Largest node
20 22 20 deleted