1 Working With BSTs
1.1 Class Business
1.2 Recursion and Put
1.3 Computing Min and Max in a BST
1.4 Min discussion
1.5 Computing Floor(key) in BST
1.6 Delete Min or Delete Max
1.7 Pre-Order Traversing a Tree
1.8 In-Order Traversing a Tree
1.9 Version : 2018/ 04/ 07

CS 2223 Apr 06 2018

Lecture Path: 16
Back Next

Expected reading: pp. 406-411
Daily Exercise:
Classical selection: Dvorak: Symphony No. 9 "New World" (1893)
Musical Selection: Vanilla Ice: Ice Ice Baby (1990)

Well, I’m ramblin’, ramblin’ ’round, I’m a ramblin’ guy, I’m ramblin’, oh, yes, oh, yes! I’m a ramblin’ guy - R-A-M-B-L-I-N apostrophe, oh yes, I’m ramblin’
Steve Martin

1 Working With BSTs

1.1 Class Business

I’ve uploaded all in-class handouts that I have distributed into the GitHub respository. Find them at the top-level folder "Class Handouts"

1.2 Recursion and Put

Page 401 of the Sedgewick book contains a nice graphic explaining how the put method works. More importantly, the accompanying text does a fine job in explaining the nature of the recursive calls that you see within the BST implementation.

----

In this example, you are adding the letter to an existing BST. Starting at the root, you recursively move down until you get to a node that is larger than you AND has no left child. Once inserted, the function recursively walks backward in response to these functions returning. Each time up the value of N is updated to reflect the proper count.

If you do not understand the recursive nature of this computation, please come to office hours.

1.3 Computing Min and Max in a BST

BSTs are an extremely versatile data structure and supports a wide range of potential functionality. We compare each of these operations using three structures already covered:

-

min/max

get

floor/ceiling

int rank

select nth

put

SortedArray

1

~log N

~log N

~log N

1

N

BST

~log N

~log N

~log N

~log N

~log N

~log N

ChainST

~N

~N/M

~N

*

*

1

Note that the BST in all cases can guarantee "~log N" behavior with reasonable distribution of the keys in the BST. Thus the reason these operations can all be performed in ~log N is because the height of a balanced binary tree is guaranteed to be logarithmic with respect to the number of keys, N, in the tree. This proposition only holds if the tree is guaranteed to be balanced and we will address that topic on Day 18 (Apr 10 2018).

1.4 Min discussion

To find the minimum key in a BST, simply start at the root node and keep following the left child until there are no more left children. That node is the minimum key in the BST.

public Key min() { return min(root).key; } private Node min (Node parent) { if (parent.left == null) { return parent; } return min(parent.left); }

This is one of the simplest methods to implement. On handout, let’s review execution performance for analysis.

Many of the recursive examples shown in this chapter can be replaced with non-recursive counterparts, but there is no immediate guarantee that this will lead to noticeably faster code.

Consider eliminating recursion from the min method as shown on the handout. This works on this simple example, but not for Floor.

1.5 Computing Floor(key) in BST

Let’s tackle a more challenging question. How about returning the keys in a BST that are closest to a target key, without actually being present in the BST? We use the mathematical concept of Floor and Ceiling as follows:

In a way, you have seen this concept in Binary Array Search. Try the following example by hand:

Search for the value 7 in a sorted array:

+—+—+—+—+—+—+—+—+—+—+—+—+ | 2 | 3 | 5 | 9 | 10| 11| +—+—+—+—+—+—+—+—+—+—+—+—+ 0 1 2 3 4 5 (0) lo mid hi (1) lo mid hi (2) lo mid hi (3) hi lo

When Binary Array Search complets without finding a value, note that the value of lo points to the entry which is the smallest value larger than the target (which makes that the ceiling of 7. To locate the floor, simply subtract 1 to find that element.

Note that the above formulation must be carefully handled when looking for the ceiling of a target greater than any element in the array, or for the floor of a target smaller than any element in the array.

Given this definition of Floor, let’s review the code.

public Key floor(Key key) { Node rc = floor(root, key); if (rc == null) return null; return rc.key; } private Node floor(Node parent, Key key) { if (parent == null) return null; int cmp = key.compareTo(parent.key); if (cmp == 0) return parent; // Found: this is floor if (cmp < 0) return floor(parent.left, key); // key smaller? try left Node t = floor(parent.right, key); // greater? parent might if (t != null) return t; // be floor, but only if return parent; // no other candidate }

1.6 Delete Min or Delete Max

Precursor to describing the real delete method which will be focus of tomorrow’s lecture. Start by recognizing that it is simple to remove a node from a tree that only has a single child (whether left or right).

It is straightforward to locate the minimum key in the tree (as we have already seen) so now we want to remove its Node from the tree

public void deleteMin() { if (root != null) { root = deleteMin(root); } } Node deleteMin(Node parent) { if (parent.left == null) { // delete occurs here return parent.right; } parent.left = deleteMin(parent.left); parent.N = size(parent.left) + size(parent.right) + 1; return parent; }

This code must make sure that it enforces the BST property as well as updating the associated N attribute with the ancestor nodes that are affected by the deletion.

1.7 Pre-Order Traversing a Tree

The most important thing about a Binary Search Tree is that all of the keys are maintained in order. And more importantly, they can be retrieved in order with a careful strategy.

Let’s start by coming up with a way of visiting every key in a BST. We will call this a PreOrder traversal. Start at the root node and do the following:

// invoke a pre-order traversal of the tree public void preorder() { preorder(root); } void preorder(Node n) { if (n == null) { return; } StdOut.println (n.key); preorder (n.left); preorder (n.right); }

Try this on a sample tree and see what you get as output.

1.8 In-Order Traversing a Tree

While pre-order is interesting, a more relevant traversal is in-order traversal:

// invoke a in-order traversal of the tree public void inorder() { inorder(root); } void inorder(Node n) { if (n == null) { return; } inorder (n.left); StdOut.println (n.key); inorder (n.right); }

Try this on a sample tree and see what you get as output.

1.9 Version : 2018/04/07

(c) 2018, George Heineman