Binary Seach Tree
@(Algorithms)[JAVA|Algorithm]
BST Node
A binary search tree(BST) node has 4 fields:
1. lChild
: left child
2. rChild:
right child
3. parent
:parent node
4. key
: the data stored in the node
Binary Search Tree property
Quote from Algorithms 4th edition by Sedgewick.
Let x
be be a node in the BST, if y
is a node in the left subtree (assume left subtree is not empty) of node x
,then y.key
< x.key
; if y
is a node in the right subtree (assume left subtree is not empty)of x
, then y.key
> x.key
.
Maximum node
public BSTNode getMax(BSTNode x){
while(x.right != null){
x = x.right;
}
return x;
}
Minimum node
To find the minimum node in the tree rooted at node x
, we simply iterates the left child of x
.
//Recursive version
public BSTNode getMin(BSTNode root) {
if (root == null)
return null;
if (root.lChild == null)
return root;
else
return getMin(root.lChild);
}
//Iterative version
Successor
The successor
of node x
is the next node of x
when performing in-order traverse. In other words, it is the node with the smallest key that is greater than x.key
.
Threre are 2 case:
1. x
has a right child, so the successor is the minimum node of x
’s right subtree
2. x
has noright child, so the smallest node greater than x
must be a node in whose left subtree lies x
. So we just iterates x
’s parent to find the root of this left subtree.
private BSTNode getSuccessor(BSTNode x) {
if (x.rChild != null)
return getMin(x.rChild);// node x has a right child
else {// back track the parent of x
BSTNode y = x.parent;
while (y != null && x == y.rChild) {//loop until y is the root or x is the left child of y, which means y is greater than x, but all nodes in the right subtree of y is greater than y
x = y;
y = y.parent;//iterates the parent
}
return y;
}
}
Predecessor
private BSTNode getPredecessor(BSTNode x){
if(x.l!=null)
return getMax(x.l);
else{
BSTNode y = x.p;
while(y != null && x == y.1){
x = x.p;
y = x.p;
}
}
}
Insertion
To insert a key
in the BST, firts you have to find the proper node y
which meets one of following :
1. if (y.key
> key
), then y.rChild
==null
, append key
on the right subtree of y
2. if (y.key
≤ key
), then y.lChild
==null
, append key
on the left subtree of y
3. if( y
== null
) , then the BST is empty, append the key on the root of the tree
/**
* @param key
* the key to insert
*/
public void insert(int key) {
BSTNode y = null;
BSTNode x = this.root;
BSTNode node = new BSTNode(key);//new a node to append
// search from the root the x that all the nodes on its left subtree is smaller than key
while (x != null) {//find the insert position by iteration
y = x;
if (key < x.key)
x = x.lChild;
else
x = x.rChild;
}
node.parent = y;//link node to y
if (y == null) // empty tree
this.root = node;
else if (key < y.key) // compare the key with the counterpart of y to determine on which branch to append
y.lChild = node;
else
y.rChild = node;//link y to node
}
Recursive version of insert
public void insert(BSTNode n, BSTNode r) {
if (r == null) {// check input
this.root = n;
}
else {
if (n.key <= r.key) {
if (r.l != null) {
insert(n, r.l);
}
else {// termination condition 1
r.l = n;
n.p = r;
}
}
else {
if (r.r != null) {
insert(n, r.r);
}
else {// Termination condiftion 2
r.r = n;
n.p = r;
}
}
}
}
Transplant
This serves for deletion operation.
transplant(u, v)
replaces the subtree rooted at node u
with the subtree rooted at node v
, u
’s parent becomes v
‘s parent ,and node v
becomes the appropriate child of u
s’ parent.
//Note: this method does not attempt to update v.lChild and v.rChild.To do so or not is determined by its caller
public void transplant(BSTNode u, BSTNode v){
if(u.parent == null)//u is the root of BST
this.root = v;
else if(u == u.p.lChild)//u is left child of its parent
u.p.lChild = v;//make v the left child of u's parent
else//u is the rChild of its parent
u.p.rChild = v;//make v the rChild of u's parent
if(v != null)
v.p = u.p;//make u's parent become v's parent
}
Deletion
To delete a node z
, which node is actually removed is depend on the number of childs that node z
has, and there are 3 cases:
1. if z
has no child, we just remove z
2. if z
has 1 child, we remove z
and make the only child of z
to be the child of z.parent
3. if z
has 2 children, then we find z
’s successor y
,which must be in z
’s right subtree, and have y
take the position of z
.The rest of z
’s original right subtree becomes y
’s new right subtree, and z
’s left subtree becomes y
’s new left subtree.
When performing deletion, these 3 cases can be concluded into 4 cases:
1. If z
has no left child, then replace z with its right child.(if z.rChild
==null
,then we deal with z
has no child; if z.rChild
!= null
, then we deal with z
has only right child.)
2. if z
has only a left child(z.rChild == null && z.lChild != null
), we simply replace z with z.lChild
.
3. Otherwise, z.lChild != null && z.rChild != null
,then we find z
’s successor y
, that lies in the right subtree of z
, and y.lChild == null
3.1. if z.rChild == y
, then replace z
by y
,and do not change y
’s right child.
3.2. Otherwise, if y
lies in the right subtree of z
, but z.rChild != y
, we first replace y
by its own right child, then replace z
by
The difference between z.rChild==y
and z.rChild!=y
is that :
1.*if z.rChild==y
and y
is the successor of z
, then we do not have to deal with z
’s right subtree(‘cause y
is the right child of z
). we can simply (1)replace z
with y
,(2)*set y
as the root of the left subtree of z
. as following :
2.*ifz.rChild!=y
and y
is the successor of z
, we should (1) replace y
with r.rChild
, (2)set y
as the root of z
’s right subtree, (3)replace z
with y
,(4)*set y
as the root of z
’s left
public void delete(BSTNode n) {
if (n.lChild == null) {
transplant(n, n.rChild);
}
else if (n.rChild == null) {
transplant(n, n.lChild);//n.lChild!=null && n.rChild==null
}
else {//n has 2 children
BSTNode s = getMin(n.rChild);// find the successor of n
if (s.parent != n) {//if s is not the direct right child of n
transplant(s, s.rChild);//replace the successor with its right child
s.rChild = n.rChild;
n.rChild.parent = s;//set n's right child to s's right child
}
transplant(n, s);//if m is the direct right child of n, then s must have no
s.lChild = n.lChild;
s.lChild.parent = s;//set n's left child to s's right child
}
}
Tree walk / Traverse
Running time is O(N) , where N is the number of the nodes.
In-order tree walk
Pre-order tree walk
Post-order tree walk
Exercise
12.3.1
Give a recursive version of BST insertprocedure.
Solution:
/**
* Recursively insert node x from node r
*/
public void insert(BSTNode x, BSTNode r){
if(x.key <= r.key){
if(r.l != null){
insert(x, r.l);
}else{//r.l == null
r.l = x;
x.p = r;
}
}else{
if(r.r != null){
insert(x, r.r);
}else{
r.r = x;
x.p=r;
}
}
}
12.3.2
Suppose that we construct a BST by repeatedly inserting distinct values to the tree. Argue that the number of the nodes examined in searching for a value in the tree (Ns) is 1 plus the number of nodes examined when the value was first inserted into the tree (Ni).
Pr:
When insert a node x into the BST at the depth of h, we have to compare h nodes. So Ni = h;
When we search for a node x
in the BST, assuming that the target node is at the depth of h, we have to compare h times to find the right branch, and at last to compare the node at the branch with node x
.The examined nodes Ns are totally h+1.
12.3.3
We can sort a given set of n numbers by first building a binary search tree containing
these numbers (using TREE-INSERT repeatedly to insert the numbers one by one) and then printing the numbers by an inorder tree walk. What are the worstcase and best-case running times for this sorting algorithm?
Solution:
The algorithm:
The worst case: Θ(N2) : when the input set of data is already sorted, thus the insert operation results a linear linked chain.
The best case: Θ(NlogN) :when the BST is strictly balanced——for input set with n items, the height of the resulted BST is logN .
12.3.4
Is the operation of deletion “commutative” in the sense that deleting x and then y
from a binary search tree leaves the same tree as deleting y and then x? Argue why
it is or give a counterexample.
Solution:
No.
12.3.5
Suppose that instead of each node x
keeping the attribute x.p
, pointing to x
’s parent, it keeps x.succ
, pointing to x
’s successor. Give pseudocode for SEARCH
, INSERT
, and DELETE
on a binary search tree T using this representation. These procedures should operate in time
O(h)
, where h is the height of the tree T . (Hint: You may wish to implement a subroutine that returns the parent of a node.)
Solution:
O(h)→O(NlogN)
, where
h
is the height and
Lemma:
To find the parent of node x
, we should find the maximum key M
in the subtree rooted at x
first. Then, we go downward to the right from M.succ.left
. When we reach x
, the node we encounter before x
is x
’s parent.
If node m
is the max of the subtree rooted at x, then m
must have no right child, hence m.succ
is the first ancestor of x
whose left child is also the ancestor of x
. If x
has no right child, then x is the max node, and all the above still satisfies.
public BSTNode getParent(BSTNode x)
{
if(x == root)
return NULL;
BSTNode max = getMax(x);//get the max key in the subtree rooted at x
BSTNode parent = max.succ;
BSTNode t = null;
if(parent != null)
t = parent.left;
else
t = root;
while(t != x){//for java, we may use t.equals() method instead
parent = t;
t = t.right;
}//after the iteration, parent points to the
return parent;
}
12.3-6
**W**hen node z
in TREE-DELETE
has two children, we could choose node y
as its predecessor rather than its successor. What other changes to TREE-DELETE
would be necessary if we did so? Some have argued that a fair strategy, giving equal priority to predecessor and successor, yields better empirical performance. How might TREE-DELETE
be changed to implement such a fair strategy?
Solution:
**W**e find the successor
when deleting node x
in BST simply to replace the position of x
.The successor
can take position, so does the predecessor
.
Here is the pseudocode of delete
:
If we try to use
predecessor
to replace
successor
, then we have the change the algorithm as follow:
Extension Excercise
1. Get depth
Given a BST, try to find the depth of the tree using recursion
Solution:
public int getDepth(BSTNode r){
if(r == null)
return 0;
else{
int depthL = getDepth(r.l);
int depthR = getDepth(r.r);
return (depthL > depthR)?(depthL+1):(depthR+1);
}
}