# L17 Binary Search Trees and AVL ```Binary Search Tree and AVL
More Trees, More Solutions
Binary Search Trees
Binary Tree Terms
a
a
b
d
h
c
e
b
f
g
m
n
c
d
o
A binary tree is balanced if the left and right
subtrees' heights differ by at most one. So the
left subtree must be balanced and the right
subtree must be balanced.
h
This binary tree is unbalanced.
Binary Search
Binary search can perform search operations on an array-based sequence that are
sorted by key
At each step, the number of candidate items is halved
This means that the algorithm terminates after O(log n) steps, as shown in find(7)
int binarySearch(int array[], int left, int right, int target){
if (right &gt;= left) {
int middle = left + ((right - left) / 2);
Recursive
Function
// target is the middle element
if (array[middle] == target) return middle;
// target is smaller than middle, it’s in left subarray
if (array[middle] &gt; target) {
return binarySearch(array, left, middle - 1, target);
}
Binary Search
// target is larger than middle, it’s in right subarray
return binarySearch(array, middle + 1, right, target);
}
// target is not in the array
return -1;
}
Binary Search Trees
A binary search tree is a binary tree storing
items, keys (or key-value entries) at its
internal nodes and satisfying the following
property:
●
●
●
●
u, v, and w are nodes
u is in the left subtree of v
w is in the right subtree of v
key(u) ≤ key(v) ≤ key(w)
An inorder traversal of a binary search trees
visits the keys in increasing order
Binary Search Trees External Nodes (Leaves)
The book believes external nodes do not store items because they serve as
“placeholders.”
This approach simplifies their search/update algorithms.
However, this is not always the case.
For this presentation, we will follow the book’s method
Just keep this in mind as you read material outside of this book
VisuAlgo - Binary Search Tree
// a binary search tree
Interface (pt. 1)
template &lt;typename E&gt;
class SearchTree {
public:
typedef typename E::Key K;
Binary Search Tree
// a key (from Entry in Maps)
typedef typename E::Value V; // a value (from Entry in Maps)
class Iterator;
// an iterator/position
// public functions
public:
Interface (pt. 2)
SearchTree();
// constructor
int size() const;
// number of entries
bool empty() const;
// is the tree empty?
Iterator find(const K&amp; k); // find entry with key k
Iterator insert(const K&amp; k, const V&amp; x); // insert (k,x)
// remove key k entry
Binary Search Tree
void erase(const K&amp; k) throw(NonexistentElement);
// remove entry at p
void erase(const Iterator&amp; p);
Iterator begin();
// iterator to first entry
Iterator end();
// iterator to end entry
// local utilities
protected:
typedef BinaryTree&lt;E&gt; BinaryTree;
Interface (pt. 3)
// position in the tree
typedef typename BinaryTree::Position TPos;
TPos root() const;
Binary Search Tree
// get virtual root
TPos finder(const K&amp; k, const TPos&amp; v); // find utility
TPos inserter(const K&amp; k, const V&amp; x);
// insert utility
TPos eraser(TPos&amp; v);
// erase utility
TPos restructure(const TPos&amp; v)
// restructure
throw(BoundaryViolation);
private:
// member data
BinaryTree T; // the binary tree
int n;
// number of entries
public:
class Iterator { // an iterator/position
private:
TPos v; // which entry
Interface (pt. 4)
public:
Iterator(const TPos&amp; vv) : v(vv) { } // constructor
const E&amp; operator*() const { return *v; }
Binary Search Tree
E&amp; operator*() { return *v; }
// are iterators equal?
bool operator==(const Iterator&amp; p) const { return v == p.v;}
};
};
Iterator&amp; operator++();
// inorder successor
friend class SearchTree;
// give search tree access
Question:
What is the successor (next node inorder) for
nodes 44 and 29? Come up with an algorithm
for both cases. Does either case have any
nodes for which it wouldn’t work?
44: if there is a right child, go once to the right and
all the way to the left.
29: if there is no right child, go up to the left until
you can no longer, then once up to
the right.
Last node has no successor.
template &lt;typename E&gt; // inorder successor
typename SearchTree&lt;E&gt;::Iterator&amp;
SearchTree&lt;E&gt;::Iterator::operator++() {
TPos w = v.right();
if (w.isInternal()) {
do {
// have right subtree?
// move down left chain
v = w;
w = w.left();
} while (w.isInternal());
Iterator ++
} else {
w = v.parent(); // get parent
while (v == w.right()) { // move up right chain
Binary Search Tree
v = w;
w = w.parent();
}
// and first link to left
v = w;
}
return *this;
}
Binary Search Trees
The increment operator contains an obvious bug
If the iterator points to the rightmost node of the entire tree, then the above function would
loop until arriving at the root, which has no parent.
The rightmost node of the tree has no successor, so the iterator should return the value end.
So, a special sentinel node is added to our tree, called the super root, which is created when
the initial tree is constructed.
The root of the binary search tree, which we call the virtual root, is made the left child of the
super root.
We define end to be an iterator that returns the position of the super root
// constructor (create the super root)
template &lt;typename E&gt;
SearchTree&lt;E&gt;::SearchTree() : T(), n(0) {
T.expandExternal(T.root());
}
// get virtual root (left child of super root)
Constructor
Binary Search Tree
template &lt;typename E&gt;
typename SearchTree&lt;E&gt;::TPos SearchTree&lt;E&gt;::root() const {
return T.root().left();
}
// REMINDER CODE: expand external node
Node* v = p.v;
// p's node
v-&gt;left = new Node;
// add a new left child
v-&gt;left-&gt;par = v;
// v is its parent
v-&gt;right = new Node;
// and a new right child
v-&gt;right-&gt;par = v;
// v is its parent
n += 2;
// two more nodes
}
Begin and End
Methods
template &lt;typename E&gt;
// iterator to first entry
typename SearchTree&lt;E&gt;::Iterator SearchTree&lt;E&gt;::begin() {
TPos v = root();
// start at virtual root
while (v.isInternal()) v = v.left(); // find leftmost node
return Iterator(v.parent());
}
// iterator to end entry
Binary Search Tree
template &lt;typename E&gt;
typename SearchTree&lt;E&gt;::Iterator SearchTree&lt;E&gt;::end() {
return Iterator(T.root()); // return the super root
}
Binary Search Trees
To search for a key k, we trace a
downward path starting at the root
The next node visited depends on
the comparison of k with the key of
the current node
If we reach a leaf, the key is not
found
Example: find(4)
// find utility
template &lt;typename E&gt;
typename SearchTree&lt;E&gt;::TPos
SearchTree&lt;E&gt;::finder(const K&amp; k, const TPos&amp; v) {
if (v.isExternal())
return v;
if (k &lt; v-&gt;key())
return finder(k, v.left());
Find
Binary Search Tree
// search left subtree
else if (v-&gt;key() &lt; k)
return finder(k, v.right()); // search right subtree
else return v;
// found it here
}
// find entry with key k
template &lt;typename E&gt;
typename SearchTree&lt;E&gt;::Iterator
SearchTree&lt;E&gt;::find(const K&amp; k) {
TPos v = finder(k, root());
// search from virtual root
if (v.isInternal())
return Iterator(v);
// found it
else
return end();
}
// didn't find it
Binary Search Trees
To perform operation put(k, o), we search for
key k (using TreeSearch)
Assume k is not already in the tree, and let w
be the leaf reached by the search
We insert k at node w and expand w into an
internal node
Example: insert(5)
// insert utility
template &lt;typename E&gt;
typename SearchTree&lt;E&gt;::TPos
SearchTree&lt;E&gt;::inserter(const K&amp; k, const V&amp; x) {
TPos v = finder(k, root());
// search from virtual root
while (v.isInternal())
v = finder(k, v.right());
Insert
Binary Search Tree
// look further
T.expandExternal(v);
v-&gt;setKey(k);
// set entry
v-&gt;setValue(x);
n++;
// one more entry
return v;
// return insert position
}
// insert (k,x)
template &lt;typename E&gt;
typename SearchTree&lt;E&gt;::Iterator
SearchTree&lt;E&gt;::insert(const K&amp; k, const V&amp; x) {
TPos v = inserter(k, x);
return Iterator(v);
}
Binary Search Trees
To perform operation erase(k), we search for
key k
Assume key k is in the tree, and let let v be the
node storing k
If node v has a leaf child w, we remove v and w
from the tree with operation
removeAboveExternal(w), which removes w
and its parent
Example: remove(4)
Binary Search Trees
Consider the case where the key k to be removed is
stored at a node v whose children are both internal
Find the internal node w that follows v in an inorder
traversal (successor)
Copy key(w) into node v
Remove node w and its left child z (which must be a
leaf) by means of operation removeAboveExternal(z)
Example: erase(3)
// remove key k entry
template &lt;typename E&gt;
void SearchTree&lt;E&gt;::erase(const K&amp; k) throw(NonexistentElement)
{
Erase (Pt. 1)
TPos v = finder(k, root());
// search from virtual root
if (v.isExternal())
throw NonexistentElement(&quot;Erase of nonexistent&quot;);
eraser(v);
// remove it
}
Binary Search Tree
// erase entry at p
template &lt;typename E&gt;
void SearchTree&lt;E&gt;::erase(const Iterator&amp; p) {
eraser(p.v);
}
// remove utility
template &lt;typename E&gt;
typename SearchTree&lt;E&gt;::TPos SearchTree&lt;E&gt;::eraser(TPos&amp; v) {
TPos w;
if (v.left().isExternal()) {
w = v.left();
Erase (Pt. 2,
Utility)
// remove from left
} else if (v.right().isExternal()) {
w = v.right();
} else {
w = v.right();
// remove from right
// both internal?
// go to right subtree
do {
w = w.left();
} while (w.isInternal());
// get leftmost node
TPos u = w.parent();
Binary Search Tree
// copy w's parent to v
v-&gt;setKey(u-&gt;key());
v-&gt;setValue(u-&gt;value());
}
// one less entry and remove w and parent
n--;
return T.removeAboveExternal(w);
}
Runtime of BST Methods?
Operation
size, empty
Time
O(1)
find, insert, erase O(h)
Want to prevent worst-case scenario:
AVL Trees
AVL Tree Definition
AVL trees are balanced
An AVL Tree is a binary search
tree such that for every internal
node v of T, the heights of the
children of v can differ by at most 1
Named after inventors AdelsonVelsky and Landis
AVL Tree Restructure (Case A, Single Rotation)
Algorithm restructure(x):
Input: node x of a BST T that has both a parent y and a grandparent z
Output: Tree T after a trinode restructuring (which corresponds to a single or double rotation) involving nodes x, y, and z
1: Let (a, b, c) be a left-to-right (inorder) listing of the nodes x, y, and z, and let (T0, T1, T2, T3) be a left-to-right (inorder) listing of the four subtrees of x, y,
and z not rooted at x, y, or z.
2: Replace the subtree rooted at z with a new subtree rooted at b.
3: Let a be the left child of b and let T0 and T1 be the left and right subtrees of a, respectively.
4: Let c be the right child of b and let T2 and T3 be the left and right subtrees of c, respectively.
AVL Tree Restructure (Case A, Single Rotation)
Input: node x of a BST T that has both a
parent y and a grandparent z
1: a = z, b = y, c = x (left-to-right inorder)
T0, T1, T2, T3 are the left-to-right inorder
listing of the four subtrees
1
3
2
4
2: Replace the subtree rooted at z with a
new subtree rooted at b.
3: Let a be the left child of b
Let T0 and T1 be the left and right subtrees
of a.
4: Let c be the right child of b
Let T2 and T3 be the left and right subtrees
of c.
AVL Tree Restructure (Case A, Single Rotation)
Input: node x of a BST T that has both a parent y and a
grandparent z
a=z
1: a = z, b = y, c = x (left-to-right inorder)
T0, T1, T2, T3 are the left-to-right inorder listing of the
four subtrees
b=y
2: Replace the subtree rooted at z with a new subtree
rooted at b.
3: Let a be the left child of b
Let T0 and T1 be the left and right subtrees of a.
4: Let c be the right child of b
Let T2 and T3 be the left and right subtrees of c.
c=x
T0
T1
T2
T3
AVL Tree Restructure (Case A, Single Rotation)
Input: node x of a BST T that has both a parent y and a
grandparent z
a=z
1: a = z, b = y, c = x (left-to-right inorder)
T0, T1, T2, T3 are the left-to-right inorder listing of the
four subtrees
b=y
2: Replace the subtree rooted at z with a new subtree
rooted at b.
3: Let a be the left child of b
Let T0 and T1 be the left and right subtrees of a.
4: Let c be the right child of b
Let T2 and T3 be the left and right subtrees of c.
c=x
T0
T1
T2
T3
AVL Tree Restructure (Case A, Single Rotation)
Input: node x of a BST T that has both a parent y and a
grandparent z
1: a = z, b = y, c = x (left-to-right inorder)
T0, T1, T2, T3 are the left-to-right inorder listing of the
four subtrees
b=y
2: Replace the subtree rooted at z with a new subtree
rooted at b.
3: Let a be the left child of b
Let T0 and T1 be the left and right subtrees of a.
4: Let c be the right child of b
Let T2 and T3 be the left and right subtrees of c.
c=x
a=z
T0
T1
T2
T3
AVL Tree Restructure (Case A, Single Rotation)
Input: node x of a BST T that has both a parent y and a
grandparent z
1: a = z, b = y, c = x (left-to-right inorder)
T0, T1, T2, T3 are the left-to-right inorder listing of the
four subtrees
b=y
2: Replace the subtree rooted at z with a new subtree
rooted at b.
3: Let a be the left child of b
Let T0 and T1 be the left and right subtrees of a.
4: Let c be the right child of b
Let T2 and T3 be the left and right subtrees of c.
c=x
a=z
T0
T1
T2
T3
AVL Tree Restructure (Case A, Single Rotation)
Input: node x of a BST T that has both a parent y and a
grandparent z
1: a = z, b = y, c = x (left-to-right inorder)
T0, T1, T2, T3 are the left-to-right inorder listing of the
four subtrees
b=y
2: Replace the subtree rooted at z with a new subtree
rooted at b.
3: Let a be the left child of b
Let T0 and T1 be the left and right subtrees of a.
4: Let c be the right child of b
Let T2 and T3 be the left and right subtrees of c.
c=x
a=z
T0
T1
T2
T3
AVL Tree Restructure (Case B, Single Rotation)
Input: node x of a BST T that has both a parent y and a
grandparent z
c=z
1: a = z, b = y, c = x (left-to-right inorder)
T0, T1, T2, T3 are the left-to-right inorder listing of the
four subtrees
b=y
2: Replace the subtree rooted at z with a new subtree
rooted at b.
T3
3: Let a be the left child of b
Let T0 and T1 be the left and right subtrees of a.
a=x
T2
4: Let c be the right child of b
Let T2 and T3 be the left and right subtrees of c.
T0
T1
AVL Tree Restructure (Case B, Single Rotation)
Input: node x of a BST T that has both a parent y and a
grandparent z
c=z
1: a = z, b = y, c = x (left-to-right inorder)
T0, T1, T2, T3 are the left-to-right inorder listing of the
four subtrees
b=y
2: Replace the subtree rooted at z with a new subtree
rooted at b.
T3
3: Let a be the left child of b
Let T0 and T1 be the left and right subtrees of a.
a=x
T2
4: Let c be the right child of b
Let T2 and T3 be the left and right subtrees of c.
T0
T1
AVL Tree Restructure (Case B, Single Rotation)
Input: node x of a BST T that has both a parent y and a
grandparent z
c=z
1: a = z, b = y, c = x (left-to-right inorder)
T0, T1, T2, T3 are the left-to-right inorder listing of the
four subtrees
b=y
2: Replace the subtree rooted at z with a new subtree
rooted at b.
T3
3: Let a be the left child of b
Let T0 and T1 be the left and right subtrees of a.
a=x
T2
4: Let c be the right child of b
Let T2 and T3 be the left and right subtrees of c.
T0
T1
AVL Tree Restructure (Case B, Single Rotation)
Input: node x of a BST T that has both a parent y and a
grandparent z
1: a = z, b = y, c = x (left-to-right inorder)
T0, T1, T2, T3 are the left-to-right inorder listing of the
four subtrees
b=y
2: Replace the subtree rooted at z with a new subtree
rooted at b.
c=z
3: Let a be the left child of b
Let T0 and T1 be the left and right subtrees of a.
a=x
4: Let c be the right child of b
Let T2 and T3 be the left and right subtrees of c.
T0
T1
T2
T3
AVL Tree Restructure (Case B, Single Rotation)
Input: node x of a BST T that has both a parent y and a
grandparent z
1: a = z, b = y, c = x (left-to-right inorder)
T0, T1, T2, T3 are the left-to-right inorder listing of the
four subtrees
b=y
2: Replace the subtree rooted at z with a new subtree
rooted at b.
c=z
3: Let a be the left child of b
Let T0 and T1 be the left and right subtrees of a.
a=x
4: Let c be the right child of b
Let T2 and T3 be the left and right subtrees of c.
T0
T1
T2
T3
AVL Tree Restructure (Case C, Double Rotation)
Input: node x of a BST T that has both a parent y and a
grandparent z
a=z
1: a = z, b = y, c = x (left-to-right inorder)
T0, T1, T2, T3 are the left-to-right inorder listing of the
four subtrees
c=y
2: Replace the subtree rooted at z with a new subtree
rooted at b.
3: Let a be the left child of b
Let T0 and T1 be the left and right subtrees of a.
4: Let c be the right child of b
Let T2 and T3 be the left and right subtrees of c.
T0
b=x
T3
T1
T2
AVL Tree Restructure (Case C, Double Rotation)
Input: node x of a BST T that has both a parent y and a
grandparent z
1: a = z, b = y, c = x (left-to-right inorder)
T0, T1, T2, T3 are the left-to-right inorder listing of the
four subtrees
a=z
c=y
2: Replace the subtree rooted at z with a new subtree
rooted at b.
T0
3: Let a be the left child of b
Let T0 and T1 be the left and right subtrees of a.
b=x
4: Let c be the right child of b
Let T2 and T3 be the left and right subtrees of c.
T1
T2
T3
AVL Tree Restructure (Case C, Double Rotation)
Input: node x of a BST T that has both a parent y and a
grandparent z
c=y
1: a = z, b = y, c = x (left-to-right inorder)
T0, T1, T2, T3 are the left-to-right inorder listing of the
four subtrees
b=x
2: Replace the subtree rooted at z with a new subtree
rooted at b.
3: Let a be the left child of b
Let T0 and T1 be the left and right subtrees of a.
a=z
4: Let c be the right child of b
Let T2 and T3 be the left and right subtrees of c.
T0
T1
T2
T3
AVL Tree Restructure (Case C, Double Rotation)
Input: node x of a BST T that has both a parent y and a
grandparent z
1: a = z, b = y, c = x (left-to-right inorder)
T0, T1, T2, T3 are the left-to-right inorder listing of the
four subtrees
b=x
2: Replace the subtree rooted at z with a new subtree
rooted at b.
a=z
c=y
3: Let a be the left child of b
Let T0 and T1 be the left and right subtrees of a.
4: Let c be the right child of b
Let T2 and T3 be the left and right subtrees of c.
T0
T1
T2
T3
AVL Tree Restructure (Case C, Double Rotation)
Input: node x of a BST T that has both a parent y and a
grandparent z
1: a = z, b = y, c = x (left-to-right inorder)
T0, T1, T2, T3 are the left-to-right inorder listing of the
four subtrees
b=x
2: Replace the subtree rooted at z with a new subtree
rooted at b.
a=z
c=y
3: Let a be the left child of b
Let T0 and T1 be the left and right subtrees of a.
4: Let c be the right child of b
Let T2 and T3 be the left and right subtrees of c.
T0
T1
T2
T3
AVL Tree Restructure (Case D, Double Rotation)
Input: node x of a BST T that has both a parent y and a
grandparent z
c=z
1: a = z, b = y, c = x (left-to-right inorder)
T0, T1, T2, T3 are the left-to-right inorder listing of the
four subtrees
a=y
2: Replace the subtree rooted at z with a new subtree
rooted at b.
T3
b=x
3: Let a be the left child of b
Let T0 and T1 be the left and right subtrees of a.
4: Let c be the right child of b
Let T2 and T3 be the left and right subtrees of c.
T0
T1
T2
AVL Tree Restructure (Case D, Double Rotation)
Input: node x of a BST T that has both a parent y and a
grandparent z
c=z
1: a = z, b = y, c = x (left-to-right inorder)
T0, T1, T2, T3 are the left-to-right inorder listing of the
four subtrees
a=y
T3
2: Replace the subtree rooted at z with a new subtree
rooted at b.
3: Let a be the left child of b
Let T0 and T1 be the left and right subtrees of a.
b=x
T0
4: Let c be the right child of b
Let T2 and T3 be the left and right subtrees of c.
T1
T2
AVL Tree Restructure (Case D, Double Rotation)
Input: node x of a BST T that has both a parent y and a
grandparent z
c=z
1: a = z, b = y, c = x (left-to-right inorder)
T0, T1, T2, T3 are the left-to-right inorder listing of the
four subtrees
b=x
T3
2: Replace the subtree rooted at z with a new subtree
rooted at b.
a=y
3: Let a be the left child of b
Let T0 and T1 be the left and right subtrees of a.
4: Let c be the right child of b
Let T2 and T3 be the left and right subtrees of c.
T0
T1
T2
AVL Tree Restructure (Case D, Double Rotation)
Input: node x of a BST T that has both a parent y and a
grandparent z
1: a = z, b = y, c = x (left-to-right inorder)
T0, T1, T2, T3 are the left-to-right inorder listing of the
four subtrees
b=x
2: Replace the subtree rooted at z with a new subtree
rooted at b.
a=y
3: Let a be the left child of b
Let T0 and T1 be the left and right subtrees of a.
4: Let c be the right child of b
Let T2 and T3 be the left and right subtrees of c.
T0
c=z
T1
T2
T3
AVL Tree Restructure (Case D, Double Rotation)
Input: node x of a BST T that has both a parent y and a
grandparent z
1: a = z, b = y, c = x (left-to-right inorder)
T0, T1, T2, T3 are the left-to-right inorder listing of the
four subtrees
b=x
2: Replace the subtree rooted at z with a new subtree
rooted at b.
a=y
3: Let a be the left child of b
Let T0 and T1 be the left and right subtrees of a.
4: Let c be the right child of b
Let T2 and T3 be the left and right subtrees of c.
T0
c=z
T1
T2
T3
AVL Tree Insertion
Insertion is as in a binary search tree
Always done by expanding an external node.
Example: Inserting 54 (Case D, Double Rotation)
AVL Tree Removal
Removal begins as in a binary search tree, which means the node removed will
become an empty external node. Its parent, w, may cause an imbalance.
Example: Remove 32 (Case A, Single Rotation)
Question:
How many different binary search trees can
store the keys {1, 2, 3}?