Professional Documents
Culture Documents
Binary Search Tree
Binary Search Tree
B C
D E F
G H I
Binary Tree
root
B C
D E F
G H I
A
root
B C
D E F
Left subtree G H I
Right subtree
Binary Tree
Recursive definition
B C
root
D E F
G H I
Left subtree
Binary Tree
Recursive definition
B C
D E F
root
G H I
Binary Tree
Recursive definition
A
root
B C
D E F
G H I
Right subtree
Binary Tree
Recursive definition
B C
root
D E F
G H I
B C
D E F
G H I
Not a Tree
Structures that are not trees.
B C
D E F
G H I
Not a Tree
Structures that are not trees.
B C
D E F
G H I
Binary Tree: Terminology
parent
A
D E F
G H I
B C
D E J F
G K H I
Level of a Binary Tree Node
The level of a node in a binary tree is defined as follows:
Root has level 0,
Level of any other node is one more than the level its parent (father).
The depth of a binary tree is the maximum level of any leaf in the
tree.
Level of a Binary Tree Node
A 0 Level 0
B 1 C 1 Level 1
D 2 E 2 F 2 Level 2
G 3 H 3 I 3 Level 3
Complete Binary Tree
A complete binary tree of depth d is the strictly
binary all of whose leaves are at level d.
0
A
B 1 C 1
D 2 E 2 F 2 G 2
H 3 I J 3 K L 3 M 3 N 3 O 3
Complete Binary Tree
A Level 0: 20 nodes
B C Level 1: 21 nodes
D E F G Level 2: 22 nodes
H I J K L Level 3: 23 nodes
Complete Binary Tree
At level k, there are 2k nodes.
Total number of nodes in the tree of depth d:
In a complete binary tree, there are 2d leaf nodes and (2d - 1) non-
leaf (inner) nodes.
j=0
d
Complete Binary Tree
If the tree is built out of ‘n’ nodes then
n = 2d+1 – 1
or log2(n+1) = d+1
or d = log2(n+1) – 1
I.e., the depth of the complete binary tree built
using ‘n’ nodes will be log2(n+1) – 1.
For example, for n=1,000,000, log2(1000001) is
less than 20; the tree would be 20 levels deep.
The significance of this shallowness will become
evident later.
Operations on Binary Tree
There are a number of operations that can be defined for a binary
tree.
If p is pointing to a node in an existing tree then
left(p) returns pointer to the left subtree
right(p) returns pointer to right subtree
parent(p) returns the father of p
brother(p) returns brother of p.
info(p) returns content of the node.
Operations on Binary Tree
In order to construct a binary tree, the following can be useful:
setLeft(p,x) creates the left child node of p. The child node contains
the info ‘x’.
setRight(p,x) creates the right child node of p. The child node
contains the info ‘x’.
Applications of Binary Trees
A binary tree is a useful data structure when two-way decisions
must be made at each point in a process.
For example, suppose we wanted to find all duplicates in a list of
numbers:
B C
Searching for Duplicates
14
15 14
14
15
4 14
15
14
4 15
9 14
4 15
14
4 15
7 14
4 15
14
4 15
18 14
4 15
14
4 15
9 18
3 14
4 15
9 18
14
4 15
3 9 18
5 14
4 15
3 9 18
14
4 15
3 9 18
16 14
4 15
3 9 18
14
4 15
3 9 18
7 16
4 14
4 15
3 9 18
7 16
20 14
4 15
3 9 18
7 16
14
4 15
3 9 18
7 16 20
17 14
4 15
3 9 18
7 16 20
17, 9, 14, 5
Searching for Duplicates
14
4 15
3 9 18
7 16 20
5 17
17, 9, 14, 5
Searching for Duplicates
14
4 15
3 9 18
7 16 20
5 17
9, 14, 5
C++ Implementation
#include <stdlib.h>
template <class Object>
class TreeNode {
public:
// constructors
TreeNode()
{
this->object = NULL;
this->left = this->right = NULL;
};
TreeNode( Object* object )
{
this->object = object;
this->left = this->right = NULL;
};
C++ Implementation
Object* getInfo()
{
return this->object;
};
void setInfo(Object* object)
{
this->object = object;
};
TreeNode* getLeft()
{
return left;
};
void setLeft(TreeNode *left)
{
this->left = left;
};
C++ Implementation
TreeNode *getRight()
{
return right;
};
void setRight(TreeNode *right)
{
this->right = right;
};
int isLeaf( )
{
if( this->left == NULL && this->right == NULL )
return 1;
return 0;
};
C++ Implementation
private:
Object* object;
TreeNode* left;
TreeNode* right;
}; // end class TreeNode
C++ Implementation
#include <iostream>
#include <stdlib.h>
#include "TreeNode.cpp"
4 15
3 9 18
7 16 20
17, 9, 14, 5
Trace of insert
p
17 14
4 q 15
3 9 18
7 16 20
17, 9, 14, 5
Trace of insert
17 14
4 p 15
q
3 9 18
7 16 20
17, 9, 14, 5
Trace of insert
17 14
4 p 15
3 9 q 18
7 16 20
17, 9, 14, 5
Trace of insert
17 14
4 15
3 9 p 18
q
7 16 20
17, 9, 14, 5
Trace of insert
17 14
4 15
3 9 p 18
7 q 16 20
17, 9, 14, 5
Trace of insert
17 14
4 15
3 9 18
7 p 16 20
q
5
17, 9, 14, 5
Trace of insert
17 14
4 15
3 9 18
7 p 16 20
5 q
17, 9, 14, 5
Trace of insert
14
4 15
3 9 18
7 p 16 20
5 node 17
4 15
3 9 18
7 16 20
17, 9, 14, 5
Trace of insert
16 14
4 p 15
3 9 18
7 16 20
17, 9, 14, 5
Trace of insert
16 14
4 15
3 9 p 18
7 16 20
17, 9, 14, 5
Trace of insert
16 14
4 15
3 9 18
7 p 16 20
17, 9, 14, 5
Cost of Search
Given that a binary tree is level d deep. How long does it take to find
out whether a number is already present?
Consider the insert(17) in the example tree.
Each time around the while loop, we did one comparison.
After the comparison, we moved a level down.
Cost of Search
With the binary tree in place, we can write a routine find(x) that
returns true if the number x is present in the tree, false otherwise.
How many comparison are needed to find out if x is present in the
tree?
We do one comparison at each level of the tree until either x is
found or q becomes NULL.
Cost of Search
If the binary tree is built out of n numbers, how many comparisons
are needed to find out if a number x is in the tree?
Recall that the depth of the complete binary tree built using ‘n’
nodes will be log2(n+1) – 1.
For example, for n=100,000, log2(100001) is less than 20; the tree
would be 20 levels deep.
Cost of Search
If the tree is complete binary or nearly complete, searching through
100,000 numbers will require a maximum of 20 comparisons.
Or in general, approximately log2(n).
Compare this with a linked list of 100,000 numbers. The
comparisons required could be a maximum of n.
Binary Search Tree
A binary tree with the property that items in the left subtree are
smaller than the root and items are larger or equal in the right
subtree is called a binary search tree (BST).
The tree we built for searching for duplicate numbers was a binary
search tree.
BST and its variations play an important role in searching
algorithms.
Traversing a Binary Tree
Suppose we have a binary tree, ordered (BST) or unordered.
We want to print all the values stored in the nodes of the tree.
In what order should we print them?
Traversing a Binary Tree
Ways to print a 3 node tree:
14
4 15
N node
L left right R
subtree subtree
(L,N,R), (L,R,N)
(N,L,R), (N,R,L)
(R,L,N), (R,N,L)
Traversing a Binary Tree
Three common ways
N node
left right
L subtree subtree R
Preorder: (N,L,R)
Inorder: (L,N,R)
Postorder: (L,R,N)
Traversing a Binary Tree
void preorder(TreeNode<int>* treeNode)
{
if( treeNode != NULL )
{
cout << *(treeNode->getInfo())<<" ";
preorder(treeNode->getLeft());
preorder(treeNode->getRight());
}
}
Traversing a Binary Tree
void inorder(TreeNode<int>* treeNode)
{
if( treeNode != NULL )
{
inorder(treeNode->getLeft());
cout << *(treeNode->getInfo())<<" ";
inorder(treeNode->getRight());
}
}
Traversing a Binary Tree
void postorder(TreeNode<int>* treeNode)
{
if( treeNode != NULL )
{
postorder(treeNode->getLeft());
postorder(treeNode->getRight());
cout << *(treeNode->getInfo())<<" ";
}
}
Traversing a Binary Tree
cout << "inorder: "; preorder( root);
cout << "inorder: "; inorder( root );
cout << "postorder: "; postorder( root );
Traversing a Binary Tree
14
4 15
3 9 18
7 16 20
5 17
Preorder: 14 4 3 9 7 5 15 18 16 17 20
Traversing a Binary Tree
14
4 15
3 9 18
7 16 20
5 17
Inorder: 3 4 5 7 9 14 15 16 17 18 20
Traversing a Binary Tree
14
4 15
3 9 18
7 16 20
5 17
Postorder: 3 5 7 9 4 17 16 20 18 15 14
Traversing a Binary Tree
12
8 15
3 14 18
9
2 10 20
17
11
Postorder: 3 5 7 9 4 17 16 20 18 15 14
Recursive Call
Recall that a stack is used during function calls.
The caller function places the arguments on the stack and passes
control to the called function.
Local variables are allocated storage on the call stack.
Calling a function itself makes no difference as far as the call stack
is concerned.
Stack Layout during a call
Here is stack layout when function F calls function F (recursively):
Return address(F)
sp
At point of call During execution of F After call
Recursion: preorder
14 preorder(14)
14
..preorder(4)
4 15 4
....preorder(3)
3
......preorder(null)
3 9 18 ......preorder(null)
....preorder(9)
9
7 16 20
......preorder(7)
7
........preorder(5)
5 17
5
..........preorder(null)
..........preorder(null)
........preorder(null)
......preorder(null)
Recursion: preorder
14 ..preorder(15)
15
....preorder(null)
4 15 ....preorder(18)
18
......preorder(16)
3 9 18 16
........preorder(null)
7 16 20 ........preorder(17)
17
..........preorder(null)
5 17 ..........preorder(null)
......preorder(20)
20
........preorder(null)
........preorder(null)
Recursion: inorder
14 inorder(14)
..inorder(4)
....inorder(3)
4 15 ......inorder(null)
3
......inorder(null)
4
3 9 18 ....inorder(9)
......inorder(7)
........inorder(5)
7 16 20
..........inorder(null)
5
..........inorder(null)
5 17
7
........inorder(null)
9
......inorder(null)
14
Recursion: inorder
14 ..inorder(15)
....inorder(null)
15
4 15 ....inorder(18)
......inorder(16)
........inorder(null)
3 9 18 16
........inorder(17)
7 16 20 ..........inorder(null)
17
..........inorder(null)
5 17 18
......inorder(20)
........inorder(null)
20
........inorder(null)
Level-order Traversal
There is yet another way of traversing a binary tree that is not
related to recursive traversal procedures discussed previously.
In level-order traversal, we visit the nodes at each level before
proceeding to the next level.
At each level, we visit the nodes in a left-to-right order.
Level-order Traversal
14
4 15
3 9 18
7 16 20
5 17
Level-order: 14 4 15 3 9 18 7 16 20 5 17
Deleting a node in BST
As is common with many data structures, the hardest operation is
deletion.
Once we have found the node to be deleted, we need to consider
several possibilities.
If the node is a leaf, it can be deleted immediately.
Deleting a node in BST
Ifthe node has one child, the node can be
deleted after its parent adjusts a pointer to
bypass the node and connect to inorder
successor.
6
2 8
1 4
3
Deleting a node in BST
The inorder traversal order has to be maintained after the delete.
6 6
2 8 2 8
1 4 1 4
3 3
Deleting a node in BST
The inorder traversal order has to be maintained after the delete.
6 6 6
2 8 2 8 2 8
1 4 1 4 1 3
3 3
Deleting a node in BST
The complicated case is when the node to be deleted has both left
and right subtrees.
The strategy is to replace the data of this node with the smallest
data of the right subtree and recursively delete that node.
Deleting a node in BST
Delete(2): locate inorder successor
2 8
1 5
4
Inorder
successor
Deleting a node in BST
Delete(2): locate inorder successor
4
Inorder
successor
Deleting a node in BST
Delete(2): copy data from inorder successor
6 6
2 8 3 8
1 5 1 5
3 3
4 4
Deleting a node in BST
Delete(2): remove the inorder successor
6 6 6
2 8 3 8 3 8
1 5 1 5 1 5
3 3 3
4 4 4
Deleting a node in BST
Delete(2)
6 6
3 8 3 8
1 5 1 5
3 4
4
Deletion
12
3 15
2 14 18
10
9 20
17
11
AVL Tree
An AVL Tree
height
5
2 8
1 4 7
3
AVL Tree
2 8 2
1 4 1
3 5 0
Balanced Binary Tree
-1
1 0
0 0 1 -1
0 0 0 0 0 0
0 0 0 0
Balanced Binary Tree
Insertions and effect on balance
-1
1 0
0 0 1 -1
0 0 B B 0 0 0 0
U1 U2 U3 U4 0 0 B B B B 0 0
-1
1 0
0 0 1 -1
0 0 B B 0 0 0 0
U1 U2 U3 U4 0 0 B B B B 0 0
-1
1 0
0 0 1 -1
0 0 B B 0 0 0 0
U1 U2 U3 U4 0 0 B B B B 0 0
A 1
B 0
T3
T1 T2 1
Inserting New Node in AVL Tree
A 2
B 1
T3
T1 T2 1
2
new
Inserting New Node in AVL Tree
A 2 B 0
A
B 1 0
T1
T3
T1 T2 1 T2 T3
2
new
new
Inorder: T1 B T2 A T3 Inorder: T1 B T2 A T3
AVL Tree Building Example
Insert(1)
1
AVL Tree Building Example
Insert(2)
2
AVL Tree Building Example
1 -2
3
AVL Tree Building Example
1 -2
3
AVL Tree Building Example
Insert(3)
1 3
AVL Tree Building Example
12
9
Insert(3) single left rotation
1 -2
3
AVL Tree Building Example
13
0
Insert(3)
1 3
AVL Tree Building Example
13
1
Insert(4)
1 3
4
AVL Tree Building Example
13
2
Insert(5)
1 3 -2
5
AVL Tree Building Example
13
3
Insert(5)
1 4
3 5
AVL Tree Building Example
13
4
Insert(6)
2 -2
1 4
3 5
6
AVL Tree Building Example
13
5
Insert(6)
2 5
1 3 6
AVL Tree Building Example
13
6
Insert(7)
2 5 -2
1 3 6
7
AVL Tree Building Example
13
7
Insert(7)
2 6
1 3 5 7
AVL Tree Building Example
13
8
Insert(16)
4
2 6
1 3 5 7
16
AVL Tree Building Example
13
9
Insert(15)
2 6
1 3 5 7 -2
16
15
AVL Tree Building Example
14
0
Insert(15)
2 6
1 3 5 16 -2
15
Cases for Rotation
14
1
Single rotation does not seem to restore the balance.
The problem is the node 15 is in an inner subtree that is too deep.
Let us revisit the rotations.
Cases for Rotation
14
2
Let us call the node that must be rebalanced .
Since any node has at most two children, and a height imbalance requires
that ’s two subtrees differ by two (or –2), the violation will occur in four
cases:
Cases for Rotation
14
3
1. An insertion into left subtree of the left child of .
2. An insertion into right subtree of the left child of .
3. An insertion into left subtree of the right child of .
4. An insertion into right subtree of the right child of .
Cases for Rotation
14
4
The insertion occurs on the “outside” (i.e., left-left or right-right) in
cases 1 and 4
Single rotation can fix the balance in cases 1 and 4.
Insertion occurs on the “inside” in cases 2 and 3 which single rotation
cannot fix.
Cases for Rotation
14
5
Single right rotation to fix case 1.
k2 k1
k1 k2
Z Level n-2 X
X Y Level n-1 Y Z
new
Level n
new
Cases for Rotation
14
6
Single left rotation to fix case 4.
k1 k2
k2 k1
X Level n-2
Z
Y Level n-1
X Y
Z
Level n
Cases for Rotation
14
7
Single right rotation fails to fix case 2.
k2 k1
k1 k2
Z Level n-2 X
X Y Level n-1 Y Z
Level n
new new
Cases for Rotation
14
8
Y is non-empty because the
new node was inserted in Y.
k2
Thus Y has a root and two
k1 subtrees.
Z
View the entire tree with four
X
subtrees connected with 3
nodes.
Y
Cases for Rotation
14
9
Y is non-empty because the
new node was inserted in Y.
k3
Thus Y has a root and two
k1 subtrees.
D
View the entire tree with four
k2
A
subtrees connected with 3
nodes.
B C
Cases for Rotation
15
0
k3
Exactly one of tree B or C
is two levels deeper than
k1
D; we are not sure which
one.
k2 Good thing: it does not
D matter.
A To rebalance, k3 cannot be
1
B C
left as the root.
2
new new’
spots
Cases for Rotation
15
1
k3
A rotation between k3
and k1 (k3 was k2 then)
k1 was shown to not work.
The only alternative is to
k2
D
place k2 as the new root.
A
This forces k1 to be k2‘s
B C
1
left child and k3 to be its
2 right child.
new new’
spots
Cases for Rotation
15
2
Left-right double rotation to fix case 2.
k3 k3
Rotate left
k1 k2
k2
k1 D
D
C
A
1 B
B C
2 A new’
k3 k2
Rotate right
k2 k1 k3
k1 D
C B C
A D
B
new
Cases for Rotation
15
4
Right-left double rotation to fix case 3.
k1 Rotate right k1
k3 k2
A A
k2 k3
D B
B C C D
Cases for Rotation
15
5
Right-left double rotation to fix case 3.
k1 Rotate left k2
k2 k1 k3
A
k3
B C D
B A
C D
AVL Tree Building Example
15
6
Insert(15)
4
2 6
1 3 5 7 k1
16 k2
X
(null)
Z
Y (null)
15
AVL Tree Building Example
15
7
Insert(15) right-left double rotation
2 6
k1
1 3 5 7
k3
16
k2
Rotate right
15
AVL Tree Building Example
15
8
Insert(15) right-left double rotation
2 6
k1
1 3 5 7
Rotate left
k2
15
k3
16
AVL Tree Building Example
15
9
Insert(15) right-left double rotation
2 6
k2
1 3 5 15
k1 k3
7 16
AVL Tree Building Example
16
0
Insert(14): right-left double rotation
4
k1
2 6
k3
1 3 5 15
k2
Rotate right
7 16
14
AVL Tree Building Example
16
1
Insert(14): right-left double rotation
4
k1
2 6 Rotate left
k2
1 3 5 7
k3
15
14 16
AVL Tree Building Example
16
2
Insert(14): right-left double rotation
4
k2
2 7
k1 k3
1 3 6 15
5 14 16
AVL Tree Building Example
16
3
Insert(13): single rotation
4 Rotate left
2 7
1 3 6 15
5 14 16
13
AVL Tree Building Example
16
4
Insert(13): single rotation
4 15
2 6 14 16
1 3 5 13
C++ code for insert
16
5
TreeNode<int>*
avlInsert(TreeNode<int>* root, int info)
{
if( info < root->getInfo() ){
root->se tLeft(avlInsert(root->getLeft(), info));
int htdiff = height(root->getLeft()) –
height(root->getRight());
if( htdiff == 2 )
if( info < root->getLeft()->getInfo() )
root = singleRightRotation( root );
else
root = doubleLeftRightRotation( root );
}
C++ code for insert
16
6
TreeNode<int>*
avlInsert(TreeNode<int>* root, int info)
{
if( info < root->getInfo() ){
root->setLeft(avlInsert(root->getLeft(), info));
int htdiff = height(root->getLeft()) –
height(root->getRight());
if( htdiff == 2 )
if( info < root->getLeft()->getInfo() )
root = singleRightRotation( root );
else
root = doubleLeftRightRotation( root );
}
C++ code for insert
16
7
TreeNode<int>*
avlInsert(TreeNode<int>* root, int info)
{ Root of left subtree may change
if( info < root->getInfo() ){
root->setLeft(avlInsert(root->getLeft(), info));
int htdiff = height(root->getLeft()) –
height(root->getRight());
if( htdiff == 2 )
if( info < root->getLeft()->getInfo() )
root = singleRightRotation( root );
else
root = doubleLeftRightRotation( root );
}
C++ code for insert
16
8
TreeNode<int>*
avlInsert(TreeNode<int>* root, int info)
{
if( info < root->getInfo() ){
root->setLeft(avlInsert(root->getLeft(), info));
int htdiff = height(root->getLeft()) –
height(root->getRight());
if( htdiff == 2 )
if( info < root->getLeft()->getInfo() )
root = singleRightRotation( root );
else
root = doubleLeftRightRotation( root );
}
C++ code for insert
16
9
TreeNode<int>*
avlInsert(TreeNode<int>* root, int info)
{
if( info < root->getInfo() ){
root->setLeft(avlInsert(root->getLeft(), info));
int htdiff = height(root->getLeft()) –
height(root->getRight());
if( htdiff == 2 )
if( info < root->getLeft()->getInfo() )
root = singleRightRotation( root );
else
root = doubleLeftRightRotation( root );
}
C++ code for insert
17
0
TreeNode<int>*
avlInsert(TreeNode<int>* root, int info)
{
if( info < root->getInfo() ){
root->setLeft(avlInsert(root->getLeft(), info));
int htdiff = height(root->getLeft()) –
height(root->getRight());
if( htdiff == 2 )
if( info < root->getLeft()->getInfo() )
root = singleRightRotation( root ); Outside case
else
root = doubleLeftRightRotation( root
inside);
case
}
C++ code for insert
17
1
else if(info > root->getInfo() ) {
root->setRight(avlInsert(root->getRight(),info));
int htdiff = height(root->getRight()) –
height(root->getLeft());
if( htdiff == 2 )
if( info > root->getRight()->getInfo() )
root = singleLeftRotation( root );
else
root = doubleRightLeftRotation( root );
}
// else a node with info is already in the tree. In
// case, reset the height of this root node.
int ht = Max(height(root->getLeft()),
height(root->getRight()));
root->setHeight( ht+1 ); // new height for root.
return root;
}
C++ code for insert
17
2
else if(info > root->getInfo() ) {
root->setRight(avlInsert(root->getRight(),info));
int htdiff = height(root->getRight()) –
height(root->getLeft());
if( htdiff == 2 )
if( info > root->getRight()->getInfo() )
root = singleLeftRotation( root );
else
root = doubleRightLeftRotation( root );
}
// else a node with info is already in the tree. In
// case, reset the height of this root node.
int ht = Max(height(root->getLeft()),
height(root->getRight()));
root->setHeight( ht+1 ); // new height for root.
return root;
}
C++ code for insert
17
3
else if(info > root->getInfo() ) {
root->setRight(avlInsert(root->getRight(),info));
int ht = Max(height(root->getLeft()),
height(root->getRight()));
root->setHeight( ht+1 ); // new height for root.
return root;
}
C++ code for insert
17
7
else if(info > root->getInfo() ) {
root->setRight(avlInsert(root->getRight(),info));
int htdiff = height(root->getRight()) –
height(root->getLeft());
if( htdiff == 2 )
if( info > root->getRight()->getInfo() )
root = singleLeftRotation( root );
else
root = doubleRightLeftRotation( root );
}
// else a node with info is already in the tree. In
// case, reset the height of this root node.
int ht = Max(height(root->getLeft()),
height(root->getRight()));
root->setHeight( ht+1 ); // new height for root.
return root;
}