Binary Search Trees

Download Report

Transcript Binary Search Trees

CS 2133: Data Structures
Binary Search Trees
Definition of a Tree
A Binary tree is a set T of nodes such
that either
1. T is empty or
2. T is partitioned into three disjoint
subsets.
T
T
Two (possibly empty) sets that are
binary trees, called left and
right subtrees of r.
T
Tl
A single node r, the root
Tr
Structure of a Tree
root
struct Node{
int value;
Node * left;
Node * right;
}
class Tree{
Node * root;
public:
Tree();//constructor
Insert(int);
Print();
}
5
4
3
6
10
7
9
Binary Search Trees
Binary Search Trees (BSTs) are an important
data structure for manipulating dynamically
changing data sets.
 Each node has the following fields:





value: an identifying field inducing a total ordering
left: pointer to a left child (may be NULL)
right: pointer to a right child (may be NULL)
p: (sometimes) pointer to a parent node (NULL for
root)
Binary Search Trees
BST property:
Value of nodes in left subtree <= roots value
Value of nodes in right subtree > roots value
 Example:

F
B
A
H
D
K
Inorder Tree Walk

What does the following code do?
TreeWalk(x)
TreeWalk(left[x]);
print(x);
TreeWalk(right[x]);
A: prints elements in sorted (increasing) order
 This is called an inorder tree walk



Preorder tree walk: print root, then left, then right
Postorder tree walk: print left, then right, then root
Inorder Tree Walk

Example:
F
B
A
H
D
How long will a tree walk take?
 Prove that inorder walk prints in
monotonically increasing order

K
Operations on BSTs: Search

Given a key and a pointer to a node, returns an
element with that key or NULL:
Node * TreeSearch(Node * ptr,int k){
if (ptr == NULL)return NULL;
if (ptr->value==k) return ptr;
if (k < ptr->value)
return TreeSearch(ptr->left, k);
else
return TreeSearch(ptr->right, k);
}
BST Search: Example

Search for D and C:
F
B
A
H
D
K
Operations of BSTs: Insert
Adds an element x to the tree so that the binary
search tree property continues to hold
 The basic algorithm




Like the search procedure above
Insert x in place of NULL
Use a “trailing pointer” to keep track of where you
came from (like inserting into singly linked list)
BST Insert: Example

Example: Insert C
F
B
H
A
D
C
K
Recursive Insert
void BST<DataType>::InsertAux(BinNodePointer & locptr, const
DataType & item)
{if(locptr==0) locptr=new BSTNode(item);
else
if (item < locptr->data)
InsertAux(locptr->left, item);
else
InsertAux(locptr->right, item);
}
BST Search/Insert: Running Time
What is the running time of TreeSearch() or
insertAux()?
 A: O(h), where h = height of tree
 What is the height of a binary search tree?
 A: worst case: h = O(n) when tree is just a
linear string of left or right children



We’ll keep all analysis in terms of h for now
Later we’ll see how to maintain h = O(lg n)
BST Destroy the tree
BinaryTree::~BinaryTree(){destroyTree(root)}
Void BinaryTree::destroyTree(TreeNode *& treePrt)
{
if (tree{tr != NULL;)
{
destroyTree(treePtr->leftChildPtr);
destroyTree(treePtr->rightChildPtr);
delete treePtr;
treePtr=NULL;
}
}
BST Operations: Delete
Deletion is a bit tricky
 3 cases:


x has no children:
 Remove

B
H
A
D
x has one child:
 Splice

x
F
out x
C
x has two children:
 Swap
x with successor
 Perform case 1 or 2 to delete it
K
Example: delete K
or H or B
Delete Algorithm
void TreeDelete(Node *& root, int x)
{ if(root!=NULL){
if(x<root->val)TreeDelete(root->left, x);
else if(x>root->val)TreeDelete(root->right, x);
else { // we have found it so lets delete it
// tree points at it right!?
// If No children we just delete it
if(root->left==NULL && root->right==NULL){
delete(root);
root=NULL;
return;
}
Delete Continued with one child
// Check to see if the node to delete has only one child
if(root->left==NULL){ // Then splice in the right side
tree_ptr=root->right;
delete root;
root=tree_ptr;
}else if (root->right==NULL){ // splice left
tree_ptr=root->left;
delete(root);
root=tree_ptr;
} else
// both children exist! so...
Delete Continued with two children
// Here root has two children
root
// We first find the successor
{ tempPtr=root->right; // Go right
// and the all the way to the left
while(tempPtr->left!=NULL){
predPtr=tempPtr;
pred
temp
tempPtr=tempPtr->left;
}
root->val=tempPtr->val; // Copy the value up to the node
if(root->right==tempPtr)root->right=tempPtr->right;
else
predPtr->left=tempPtr->right;
delete tempPtr;
BST Operations: Delete
Why will case 2 always go to case 0 or case 1?
 A: because when x has 2 children, its
successor is the minimum in its right subtree
 Could we swap x with predecessor instead of
successor?
 A: yes. Would it be a good idea?
 A:See the Eppinger paper!!

Sorting With Binary Search Trees

Informal code for sorting array A of length n:
BSTSort(A)
for i=1 to n
TreeInsert(A[i]);
InorderTreeWalk(root);

What will be the running time in the


Worst case?
Average case?
Left Child Right Sibling Representation
F
F
B
B
A
H
D
H
H
A
D
C
C
RCLS format
Normal format
Really is a binary way to represent an n ary tree.
Also each child has a pointer to its parent.
K
Recursive Traversal of a LCRS Tree
F
Traverse(t)
B
H
{
if (t != NULL)
A
D
then Traverse(t.child())
H
If (t.sibling() !=NULL)
then Traverse(t.sibling())
C
}
Algorithm BT->LCRS Format
Can you design an algorithm that will convert
a binary tree to the left child right sibling
format?
Saving a binary search tree in a file
We can do this so the tree gets restored to the
original shape. How do we do this?
save in preorder !
 Or we can restore it in a balanced shape
save in inorder and ?

Loading a balanced tree from
a sorted list
readTree(Node *& treePtr,int n)
{
if (n>0)
{ // read in the left subtree
treePtr=new Node(NULL,NULL);
readTree(treePtr->left,n/2);
cin>> treePtr->val;
// read in the right subtree
readTree(treePtr->right,(n-1)/2);
}
Level Order Traversal
How do you perform an level by level traversal?
F
Q.enq(root)
While(Q not empty){
B
x = Q.deq();
Print x;
A
H
D
K
Q.enq(left_child);
Q.enq(right_child);
}
NOTE: This uses a queue as its support data structure
Huffman Codes
Variable length coding schemes. These schemes
represent characters that occur more frequently
with shorter codes. We thus attempt to minimize
the expected length of the code for the character.
n
Expected length =
w1l1  w2l2  ...  wnln   wi li
i 1
The weights represent the probability of
occurrence of the character within the coded
string. It is a value between 0 and 1.
Constructing the Code
1. Initialize a list of one-node binary trees
containing the weights of each char
2. Do the following n-1 times
Find two trees T’ and T’’ in this list with
minimal weight
Replace these two trees with a binary tree
whose root is w’+w’’, and label the
pointers to these trees with 0 and 1
W’+w’’
0
T’
1
T’’
Example Construction
1.0
A=00
B=01
C=10
D=110
E=1110
F=1111
1
Also,this is
Immediately
Decodable!
.40
1
0
.20
0
0
.60
0
1
.10
1
0
1
.35
.25
.20
.10
.08
.02
A
B
C
D
E
F
Expression Trees
+
*
+
W
+
X
3
Y
5
W + 5 * X + 3 + Y
inorder traversal
((W+5)*X + (3+Y))
with parens
How about preorder and postorder traversals?
Problem
A preorder traversal of a binary tree produced
ADFGHKLPQRWZ
and an inorder traversal produced
GFHKDLAWRQPZ
Draw the binary tree.
Program to Build Tree
void BuildTree(string in,string post)
{
char root;
int len=in.length();
// NOTE: Last char of postorder traveral is the root of the tree
if(len==0)return;
root=post[len-1];
cout<<root; // print the root
// Search for root in the inorder traversal list
int i=0;
while(in[i]!=root)i++; // skip to the root
0 1 2 . . .
substr(start, len)
// i now points to the root in the inorder traversal
// The chars from 0 to i-1 are in the left subtree and
// the chars from i+1 to len_in-1 are in the right sub tree.
// Process left sub tree
BuildTree(in.substr(0,i),post.substr(0,i));
//Process right sub tree
BuildTree(in.substr(i+1,len-i-1), post.substr(i,len-i-1));
}