0% found this document useful (0 votes)
63 views45 pages

CSE 12 Binary Search Trees

The document discusses binary search trees (BSTs). Key points include: - A BST is a binary tree that maintains the property that all left descendants of a node are less than the node's value and all right descendants are greater. - Common BST operations like contains, add, and remove can be implemented recursively or iteratively by traversing the tree. - An iterator can perform an inorder traversal of a BST to iterate over its elements in sorted order.

Uploaded by

ShengFeng
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
63 views45 pages

CSE 12 Binary Search Trees

The document discusses binary search trees (BSTs). Key points include: - A BST is a binary tree that maintains the property that all left descendants of a node are less than the node's value and all right descendants are greater. - Common BST operations like contains, add, and remove can be implemented recursively or iteratively by traversing the tree. - An iterator can perform an inorder traversal of a BST to iterate over its elements in sorted order.

Uploaded by

ShengFeng
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 45

CSE 12

Binary Search Trees


A Binary Search Tree ADT

A Linked Implementation of Binary Search Tree


Binary Search Tree Operation Time Costs
The Importance of Being Balanced
AVL trees

15

Binary Search Trees


A binary search tree (BST) is a tree
that has these invariants:
Structural property: A BST is a binary tree
Ordering property: For any node X in the tree:
all the data elements in Xs left subtree are
less than the data element in X,
and all the data elements in X's right subtree are
greater than the data element in X

A total order must apply to data elements in a BST...


they must be comparable to each other!
2

Binary Search Tree recursive defn


Recursive definition of BST:
A Binary Search Tree (BST) is a binary tree that
Is the empty tree, or

Is a node containing an element E,


whose left subtree is a BST
containing only elements less
than E,
and whose right subtree is a
BST containing only elements
greater than E
In a BST, the element in any node 'splits' the elements in
the node's left and right subtrees.
A BST does not contain duplicate elements.
15-3/43

Binary Search Tree ADT


Description
This ADT describes a binary search tree (BST) whose elements
are of a type which has a total order defined. Duplicates are not
allowed.
Properties
1. A BST is a binary tree.
2. For each node X, all the elements in the Xs left subtree are less
than Xs element, and all the elements in Xs right subtree are
greater than Xs element. This is the binary search tree ordering
property.
3. The elements of a BST must be comparable, i.e. totally ordered.

Binary Search Tree ADT


Attributes
size: The number of nodes in this BinarySearchTree.
root: The root of this BinarySearchTree; or null when the tree is
empty.
Operations

BinarySearchTree ( )
pre-condition:
none
responsibilities: constructor; create an empty BinarySearchTree
post-condition:
size is set to 0
root is set to a null value
returns:
nothing
5

Binary Search Tree ADT


add ( Type element )
pre-condition:
element is not null, is not already in the tree and is
comparable to the other elements in the tree
responsibilities: add an element to the tree such that the BST properties are
maintained
post-condition:
size is incremented by 1; element is added to the tree
returns:
nothing
exception:
if element is null, is already in the tree or is not comparable to other
elements in the tree

remove( Type element )


pre-condition:
element is not null and is comparable to other elements in the tree
responsibilities: remove element from the tree such that the BST properties are
maintained. If the target is not in the tree, nothing happens and the
tree is not changed
post-condition:
if element was in the tree, size is decremented by 1 and element is
removed from the tree
returns:
nothing
6

Binary Search Tree ADT


contains( Type target )
pre-condition:
target is not null
responsibilities: determine if target is stored in this tree
post-condition:
the tree is unchanged
returns:
true if target is found in this tree, false otherwise
exception:
if target is null

iterator ()
pre-condition:
responsibilities:

post-condition:
returns:

none
create and return an Iterator for this tree.
The iterator performs an
inorder traversal of this trees elements
the tree is unchanged
an Iterator for this tree

Tree traversals

Recall the binary tree traversal algorithms: preorder, inorder,


postorder, level order

Note that a inorder traversal of a binary search tree visits the


trees elements in sorted order!

This suggests a possible sorting algorithm: start with an


empty BST; add elements to be sorted; do an inorder
traversal of the BST to retrieve the elements in sorted order

That algorithm does work, but it has poor worst-case time


cost (more on this later)
8

Iterator for a BST


In the Collections framework, a container supplies the client
with an iterator object the client uses iterate over the elements
stored in the container
this is Javas implementation of the Iterator pattern

Similarly, we want a BST to provide a client with an iterator that


the client can use to iterate over elements stored in the BST, in
sorted order
This TreeIterator will perform an inorder traversal of the tree,
however it cannot do it by calling the usual recursive inorder
traversal method

Why?

Tree Iterator Methods


Instead, the Tree Iterator will simulate the recursive calls
of an inorder traversal by maintaining its own stack
This is a nice application of a stack data structure
Pseudocode: TreeIterator()
create the stack
node = this trees root
while node is not null
// check for base case
push node onto the stack
node = nodes left child // recursive caseleft
methods continued
on next slide
10

Tree Iterator Methods


Pseudocode: hasNext()
true if the stack is not empty false otherwise
Pseudocode: next()
if the stack is empty
throw an exception
node = pop stack
value is nodes element
node = nodes right child // recursive caseright
while node is not null
// check for base case
push node onto the stack
node = nodes left child
// loop post-condition: base case metfound a null left child
return value
11

The BSTNode Class


1
2
3
4
5
6
7
8
9
10
11

/**
* A binary search tree is composed of <tt>BSTNode</tt>s.
* The element stored must be comparable with other
* elements in the BST.
*/
public class BSTNode<E extends Comparable<? super E>> {
protected BSTNode<E> parent;
Note: the data fields are
protected BSTNode<E> leftChild;
protected BSTNode<E> rightChild; protected instead of
private. Why?
protected E element;

Plus two constructors:


-one that takes a data element to store in the node
-one that takes a data element and references to become the
nodes left and right subtrees
12

The contains() method


The contains() method will search the BST for a given data
value
BSTs are called binary search trees for a reason: the
ordering property makes searching in them easy
Start at the root node. Compare the data you are searching
for to the data in the node:
If the data in the node is greater, continue searching at the left child of
the node
If the data in the node is less, continue searching at the right child of
the node
Otherwise they are equal, and you have found it!
(Think: what indicates failure in the search?)

This searching can be done iteratively, or recursively


13

Helper methods for BST methods


The BinarySearchTree interface specifies that the public
add() , remove(), and contains() methods only take
one argument: a data element
These operations have a natural recursive implementation,
but...
Recursive versions would take at least two arguments: a data
element, and a node
This is because what happens in the recursive case always
depends on the combination of the data at the current
position (node) in the tree, and the target data element
So: have the public method call a recursive internal utility
helper method using the trees root as the starting point
14

contains() a recursive method


contains( node,
target ) =

false, if node is null

base case failure

true, if nodes element is equal to the target


base case success
contains( nodes left child, target), if target < nodes element recursive case left
contains( nodes right child, target), if target > nodes element recursive case right

where:
node the root of the subtree to search (initially the root of the tree)
target the element for which we are searching

Call and return paths for successful and unsuccessful contains() calls
15

contains(): recursive method


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

/**
* Determine if <tt>target</tt> is in the tree.
* @param target the element to look for; cant be <tt>null</tt>
* @return <tt>true</tt> if found, <tt>false</tt> otherwise
* @throws SearchTreeException if <tt>target</tt> is <tt>null</tt>
*/
public boolean contains( E target) {
This is the public
if ( target == null )
version clients see
throw new SearchTreeException();
return contains( this.root(), target );
}

Does the subtree rooted at node contains target?

protected boolean contains( BSTNode<E> node, E target ){


if ( node == null ) // base case failure
return false;
int compareResult = target.compareTo( node.element );
if ( compareResult < 0 ) // recursive case left
return contains( node.leftChild, target );
else if ( compareResult > 0 ) // recursive case right
return contains( node.rightChild, target );
else
return true; // base case success

This is the private


version that does
the work
Note direct access
to nodes data
fields

}
16

The add() method


Any time you are implementing an add or insert method for a
data structure, you have to make sure that:
The find or contains method will be able to find the new data you
added
The find or contains method will still be able to find all the other data
that is in the structure as well

This is easy to do in a BST:


Use the contains() methods algorithm to search for the data item to be
added
If you find it is already in the tree, you are done; BSTs dont contain
duplicates
Otherwise, you will find a null child reference where the new data item
would be, if it were in the tree. So create a new node and put it there!

The add() method can be implemented iteratively, or


recursively
17

add(): recursive method


BSTNode( element ), if node is null
add (node,
element ) =

// base case success do insertion

exception, if element == node.element


// base case failure no duplicates
node.leftChild = add( node.leftChild,
element ) if element < node.element //recursive case left
node.rightChild = add( node.rightChild,
element ) if element > node.element // recursive case right

where:
node
the root of the subtree to add (initially it is the root of the tree)
element the element to be added to the tree

Important!
Note that a node
reference is passed
back up the tree all
the way to the root
(a) The path followed to
find 17s insertion point.

(b) The new node is always


inserted at a leaf position
18

add() a recursive method


10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

public void add( E element ) {


if ( element == null )
throw new SearchTreeException();
setRoot( add( null, this.root(), element ) );
size++;
}

Note that we set the root to be a


reference to the node returned by
the original call to add()

protected BSTNode<E> add( BSTNode<E> parent, BSTNode<E> node, E element ){


if ( node == null ) { // base case
node = new BSTNode<E>( element );
node.parent = parent;
}
else { // recursive cases
int compareResult = element.compareTo( node.element );
if ( compareResult < 0 ) // recursive case left
Parent-child
node.leftChild = add( node, node.leftChild, element );
link will be reelse if ( compareResult > 0 ) // recursive case right
established as
node.rightChild = add( node, node.rightChild, element );
the recursion
else
unwinds
throw new SearchTreeException( "Duplicate element: " +
element.toString() );
}
return node; return a reference to this node to the caller (which will be this nodes parent)
}
19

add() a recursive method

Establishing the parent-child


link as the recursion unwinds

20

Implementing remove()
Removing a node is a little trickier
Need to keep the tree connected, and
Need to maintain the BST invariants
One way to do it is to consider 3 separate cases:
Case 1 The node to remove is a leaf
Case 2 The node to remove is an internal node with one
child
Case 3 The node to remove is an internal node with two
children

21

remove(): Case 1 removing a leaf

Removing a leaf node just requires


updating its parents child link to be
null: snip off the leaf

22

remove(): Case 2 target has 1 Child

Removing a node with one move the single


child into its parents position

23

remove(): Case 3 target has 2 Children

15s successor
in the tree is 20

Removing a node with two children from a


binary search tree replace the value in the
target node with the value in its successor in
the tree, then delete that successor
The successor of a node in a BST cannot have 2 children
(why?) so either Case 1 or Case 2 apply to it
24

The Need for Balance


A Binary Search Tree can be badly unbalanced, giving a
time cost n in the worst case

Bad tree!

Good tree!

Same number of elements in each tree, but very


different search times (in the worst case)
25

The Need for a Balanced Search Tree


A badly unbalanced BST is no better than a linked list!
A complete binary tree would be much better
But in a BST, maintaining completeness as a structural
invariant can have high time cost:
An add() or remove() of one node can take n steps
to restore an n-node binary search tree as a
complete binary tree

When the costs outweigh the benefits, we must consider


alternatives...
26

AVL Trees
An AVL tree is a BST with the following additional
structural balance property:
For every node X, the height of Xs left and right
subtrees differ by no more than 1

It turns out that this additional invariant implies that the


height of an AVL tree with n nodes is about 1.4 log2n in the
worst case
The add() and remove() methods are more complicated to
code because they must maintain this invariant, but still take
number of steps proportional to tree height in the worst case,
so only O(log2n) time
27

AVL Trees: a kind of balanced tree


An AVL tree is a BST with the following additional
structural balance property:
For every node X, the heights of Xs left and right
subtrees differ by no more than 1

It turns out that this additional invariant implies that the


height of an AVL tree with n nodes is about 1.4 log2n in
the worst case
The add() and remove() methods are more complicated
to code because they must maintain this invariant, but still
take number of steps proportional to tree height in the
worst case, so only O(log2n) time
28

AVL Trees: Height and Balance


An empty subtree has a height of 0
A leaf always has a balance of 0 and a height of 1
An internal nodes height is the height of its taller subtrees plus 1:
node.height = (height of taller of two subtrees) + 1
An internal nodes balance is the height of its left subtree minus the
height of its right subtree:
node.balance = height left subtree height right subtree
A balance of 1 means
the left subtree is deeper
A balance of -1
means the right
subtree is deeper

A balance of 0 means
the subtrees have the
same height
A balance >1 or <-1 means
the tree is no longer AVL15-29/43
balanced

Detecting Imbalance

(a) The AVL tree before add(22)

(b) The AVL tree after add(22). Now begin


recomputing heights and balances

(c) The recomputation occurs


moving up the tree

(d) An imbalance is found at 25,


where balance becomes 2
30

Fixing imbalance: AVL rotations

When the AVL balance property is violated because of


insertion of a new node, the property can be restored
by moving nodes in the tree around in a certain way

The technique for doing this uses AVL rotations

An AVL rotation moves 3 nodes, so one AVL rotation


takes constant O(1) time, independent of the number of
nodes in the tree

Any AVL rotation in a BST is guaranteed to preserve


the BST ordering property

This makes AVL rotations useful in other balanced


search tree approaches: red-black trees, randomized
search trees, splay trees, etc.
31

AVL rotations

32

Restoring AVL Balance: Cases


When an imbalance is discovered, it falls into
one of four special cases
Each case can be 'fixed' with one or two AVL
rotations
Case LL The balance at Xs left child, XL, is 1, so the
Left childs Left subtree, rooted at XLL, is taller
Case RL The balance at Xs right child, XR, is 1, so
the Right childs Left subtree, rooted at XRL, is taller
Case RR The balance at Xs right child, XR, is 1, so
the Right childs Right subtree, rooted at XRR, is taller
Case LR The balance at Xs left child, XL, is 1, so
the Left childs Right subtree, rooted at XLR, is taller
33

Case LL
The balance at Xs left child, XL, is 1, so Xs Left childs
Left subtree rooted at XLL is taller

34

Case LR
The balance at Xs left child, XL, is 1, so Xs Left childs
Right subtree rooted at XLR is taller

35

Case RR
The balance at Xs right child, XR, is 1, so the Right
childs Right subtree rooted at XRR is taller

This is the mirror


image of Case LL

36

Case RL
The balance at Xs right child, XR, is 1, so the Right childs
Left subtree rooted at XRL is taller

This is the mirror


image of Case LR

37

Case LL Rotation
Solution: make a single right (clockwise) rotation around X by
moving the tree rooted at XL up a level such that:
X becomes XLs right child
XLs right child (T2) becomes Xs left child
XL is the new root of the subtree

(a) An LL imbalance at X

(b) Balance is restored by a


right (clockwise) rotation

Balance is restored and the BST property is retained: T2 and XL are moved relative to X, and what we
know about the values in T2 is this: XL < values in T2 < X
Thus T2 must be between XL (on the left) and X (on the right) in a BST. This is true in the rebalanced
tree, so the rebalanced tree is still a BST.
38

Case LL Rotation Example

(a) An AVL tree with an


LL imbalance at X

(b) The AVL tree after a


right rotation around X

39

Case LR Double Rotation


Solution: We need a double rotation (which is just two
AVL rotations, one after the other)
a single left (counterclockwise) rotation of XLR is done around XL. This
moves XLs right child, XLR, up the tree
XLRs left subtree (T2L) becomes the right subtree of XL
XLRs right subtree (T2R) moves up the tree with XLR
The result of this left rotation does not restore the balance at X
we now do a single right (clockwise) rotation around X.
as we saw in the LL case, this moves Xs left child up the tree a level while
moving X down a level

40

Case LR Double Rotation

(a) Original unbalanced tree.


The dotted arc indicates that a
left (counterclockwise)
rotation of XLR around XL
will be done to produce the
tree in (b)

(b) The tree is still unbalanced at


X. The dotted arc shows the
right (clockwise) rotation of XLR
around X to produce the
balanced tree in (c)

(c) The tree is


AVL-balanced
and

second rotation

first rotation

XL < values in T2L < XLR < values in T2R < X


41

AVL Tree add()


Starts out as for ordinary BST:
Recursively descend the tree looking for the leaf position
where the new value will be inserted
Difference:
as the recursion unwinds, moving back up the tree,
heights and balances need to be recomputed
if an imbalance is discovered, its type (LL, RR, LR, RL)
needs to be identified and then rotations performed to
restore balance

42

AVL Tree remove()


Trickier than remove() for ordinary BST
As with ordinary BST, replace the target with its successor
Complication:
Need to recompute heights and balances as the recursion
unwinds
BUT the recursive calls will not have descended all the
way to the successor node
How to handle recomputation of heights and balances
from the successor node up?

43

AVL Tree remove()


(a) The recursive calls to remove()

will reach the target node (20). The


targets successor (25) is below the
target in the tree

(b) A separate mechanism is


needed to adjust the tree
between the successor
(following its removal) and
the target
44

Next time

Compilers and Interpreters


Parse Trees and Abstract Syntax Trees (AST's)
Creating and Evaluating AST's
The Table ADT and Symbol Tables

Reading: Gray, Ch 11

45

You might also like