Ch07 BST
Ch07 BST
CS401
• A tree is a nonlinear
structure in which
each node is capable
of having many
successor nodes,
called children.
• Trees are useful for
representing lots of
varied relationships
among data items.
Definitions
• Tree A structure with a unique starting node
(the root), in which each node is capable of
having multiple successor nodes (its children),
and in which a unique path exists from the root
to every other node.
• Root The top node of a tree structure; a node
with no parent
• Parent node The predecessor node of a node
is its parent
• Subtree A node and all of its descendants form
a subtree rooted at the node
Examples
• The root is A
• The children of A are B,
F, and X
• The parent of Q is X
• The leftmost subtree of
X contains H and P
Required
• A tree’s subtrees
must be disjoint
• There is a unique
path from the root
of a tree to any
other node of the
tree
Not a Tree →
Not a Tree
Manager Chef
Brad Carol
LEAF NODES
Owner
LEVEL 0 Jake
Manager Chef
Brad Carol
Manager Chef
Brad Carol
LEVEL 2
Waitress Waiter Cook Helper
Joyce Chris Max Len
Owner
Jake
Manager Chef
Brad Carol
Manager Chef
Brad Carol
RIGHT SUBTREE
OF ROOT NODE
A Binary Tree
V
Q L
E T A
K S
How many leaf nodes? 3
Q L
E T A
K S
How many descendants of Q?
V 3
Q L
E T A
K S
How many ancestors of K?
V 3
Q L
E T A
K S
Implementing a Binary Tree
with Reference links
V
Q L
E T A
K S
7.2 Binary Search Trees
• Binary tree A
tree in which
each node is
capable of having
two child nodes,
a left child node
and a right child
node
A Binary Tree
A Binary
Search
Tree
These trees facilitate
searching for an
element.
Binary search tree
• A binary tree in which the key
value in any node
– is greater than or equal to the
key value in its left child and any
of its descendants (the nodes in
the left subtree)
– is less than the key value in its
right child and any of its
descendants (the nodes in the
right subtree)
• We call this the “binary
search tree” property
BST
A special kind of binary tree in which:
‘E’
Inserting ‘F’ into the BST
‘J’
‘E’ >
‘F’
Inserting ‘T’ into the BST
‘J’
>
‘E’ ‘T’
‘F’
Inserting ‘A’ into the BST
‘J’
‘A’ ‘F’
What binary search tree . . .
‘A’
Binary search tree . . .
‘J’
‘E’ ‘T’
‘K’ ‘P’
‘E’ ‘T’
‘S’
Traverse manner
• In linear linked list, set a temporary
reference to the first element and follow
forward and backward.
• Then how about tree? Nodes (elements)
in a tree do not have linear relationship.
• Find good ways from next slides.
Binary Tree Traversals
• Preorder traversal: Visit the
root, visit the left subtree, visit
the right subtree:
– DBACFG
• Inorder traversal: Visit the left
subtree, visit the root, visit the
right subtree:
– ABCDFG
• Postorder traversal: Visit the
left subtree, visit the right
subtree, visit the root:
– ACBGFD
Visualizing Binary Tree Traversals
Three
Binary Tree
Traversals
7.3 – The Binary Search Tree
Interface
• Our binary search trees
– are similar to the sorted lists of Chapter 6
– implement this text’s CollectionInterface
– implement the Java Library’s Iterable interface
– are unbounded, allow duplicate elements, and
disallow null elements
– support max and min operations
– support preorder, inorder, and postorder traversals
package ch07.trees;
import ch05.collections.CollectionInterface;
import java.util.Iterator;
T min();
// If this BST is empty, returns null;
// otherwise returns the smallest element of the tree.
T max();
// If this BST is empty, returns null;
// otherwise returns the largest element of the tree.
Constructor:
public BSTNode(T info)
{
this.info = info;
left = null;
right = null;
}
import ch04.queues.*;
import ch02.stacks.*;
import support.BSTNode;
public T min()
// If this BST is empty, returns null;
// otherwise returns the smallest element of the tree.
{
if (isEmpty())
return null;
else
{
BSTNode<T> node = root;
while (node.getLeft() != null)
node = node.getLeft();
return node.getInfo();
}
}
7.5 Iterative versus Recursive
Method Invocations
• Trees are inherently recursive; a tree consists of
subtrees
• In this section we look at recursive and iterative
approaches to the size method
• We then discuss the benefits of recursion versus
iteration for this problem
Recursive Approach
• We create a public method, size, that calls a private recursive
method, recSize and passes it a reference to the root of the tree.
public T next()
{
if (!hasNext())
throw new IndexOutOfBoundsException("illegal invocation of next …
return infoQueue.dequeue();
}
} }
preOrder and postorder traversals
private void preOrder(BSTNode<T> node, LinkedQueue<T> q)
{
if (node != null)
{
q.enqueue(node.getInfo());
preOrder(node.getLeft(), q);
preOrder(node.getRight(), q);
}
}
can be interpreted as “Set the reference of the root of this tree to the
root of the tree that is generated when element is added to this tree.”
The add method
private BSTNode<T> recAdd(T element, BSTNode<T> node)
// Adds element to tree rooted at node; tree retains its BST property.
{
if (node == null)
// Addition place found
node = new BSTNode<T>(element);
else if (element.compareTo(node.getInfo()) <= 0)
node.setLeft(recAdd(element, node.getLeft())); // Add in left subtree
else
node.setRight(recAdd(element, node.getRight())); // Add in right subtree
return tree;
}
Note: We can remove one of the tests if we notice that the action taken when the
left child reference is null also takes care of the case in which both child references
are null. When the left child reference is null, the right child reference is returned. If
the right child reference is also null, then null is returned, which is what we want if
they are both null.
The removeNode method
private BSTNode<T> removeNode(BSTNode<T> node)
// Removes the information at node from the tree.
{
T data;
if (node.getLeft() == null)
return node.getRight();
else if (node.getRight() == null)
return node.getLeft();
else
{
data = getPredecessor(node.getLeft());
node.setInfo(data);
node.setLeft(recRemove(data, node.getLeft()));
return node;
}
}
The getPredecessor method
• The logical predecessor is the maximum value in node’s
left subtree.
• The maximum value in a binary search tree is in its
rightmost node.
• Therefore, given node’s left subtree, we just keep
moving right until the right child is null.
• When this occurs, we return the info reference of the
node.
private T getPredecessor(BSTNode<T> subtree)
// Returns the information held in the rightmost node of subtree
{
BSTNode temp = subtree;
while (temp.getRight() != null)
temp = temp.getRight();
return temp.getInfo();
}
Our Binary
Search Tree
Architecture
7.8 Binary Search Tree
Performance
• A binary search tree is an appropriate structure for many
of the same applications discussed previously in
conjunction with sorted lists.
• Similar to a sorted array-based list, it can be searched
quickly, using a binary search.
• Similar to a linked list, it allows insertions and removals
without having to move large amounts of data.
• There is a space cost - the binary search tree, with its
extra reference in each node, takes up more memory
space than a singly linked list.
Text Analysis Experiment Revisited
Text Analysis Experiment Revisited
• Due to operation complexity, performance gains
from the binary search tree are only clearly
evident as the size of the file increases.
• The table also reveals a serious issue when the
binary search tree structure is used for the Linux
Word file. The application bombs—it stops
executing and reports a “Stack overflow error.”
• The problem is that the underlying tree is
completely skewed.
Insertion
Order and
Tree Shape
Linux Word
File is like this →
Testing the Binary Search Tree
operations
• The code for the entire BinarySearchTree class is
included with the rest of the book files.
• It provides both the recursive size method, and the
iterative version (size2).
• We have also included an interactive test driver for the
ADT called ITDBinarySearchTree.
– it allows you to create, manipulate, and observe trees containing
strings.
– it also supports a print operation that allows you to indicate one
of the traversal orders and “prints” the contents of the tree, in
that order.
• You are invited to use the test driver to test the various
tree operations.
8.7 Comparing Binary Search Trees to Linear Lists
Binary Search Tree Array-Based Linked List
Linear List
Class constructor O(1) O(N) O(1)
isEmpty O(1) O(1) O(1)
reset O(N) O(1) O(1)
getNext O(1) O(1) O(1)
contains O(log2N) O(log2N) O(N)
get
Find O(log2N) O(log2N) O(N)
Process O(1) O(1) O(1)
Total O(log2N) O(log2N) O(N)
add
Find O(log2N) O(log2N) O(N)
Process O(1) O(N) O(N)
Total O(log2N) O(N) O(N)
remove
Find O(log2N) O(log2N) O(N)
Process O(1) O(N) O(1)
Total O(log2N) O(N) O(N)
Balancing a Binary Search Tree
• A beneficial addition to our Binary Search Tree
ADT operations is a balance operation
• The specification of the operation is:
public balance();
// Restructures this BST to be optimally balanced
Same as original!!!
To Ensure a Balanced Tree
• Even out as much as possible, the number
of descendants in each node’s left and
right subtrees
• First insert the “middle” item of the inOrder
array
– Then insert the left half of the array using the
same approach
– Then insert the right half of the array using the
same approach
Balance
Iterator iter = tree.getIterator(Inorder ) Our Balance Tree
int index = 0
while (iter.hasnext())
Algorithm
array[index] = iter.next( )
index++
tree = new BinarySearchTree()
tree.InsertTree(0, index - 1)
InsertTree(low, high)
if (low == high) // Base case 1
tree.add(array[low])
else if ((low + 1) == high) // Base case 2
tree.add(array[low])
tree.add(array[high])
else
mid = (low + high) / 2
tree.add(array[mid])
tree.InsertTree(low, mid – 1)
tree.InsertTree(mid + 1, high)
Using
recursive
insertTree
7.9 Application: Word Frequency
Counter
• In this section we develop a word frequency
counter that uses our binary search tree
• Read a text file and generate an alphabetical
listing of the unique words, along with a count of
how many times each word occurs
• We allow users to specify a minimum word size
and a minimum frequency count
• Present a few summary statistics
package support;
import java.text.DecimalFormat;
}
word = newWord; freq = 0;
The WordFreq
public String getWordIs(){return word;}
Class
public int getFreq(){return freq;}
• R Trees:
Application-Specific Variations
• Tries/Prefix
Trees:
Balanced Search Trees
• The Binary Search Tree ADT presented in this
chapter is an excellent collection structure for
holding “random” data, but suffers from one
major weakness—it can become unbalanced.
• Many search tree variations have been invented
to address this weakness, each in turn with their
own strengths and weaknesses.
Balanced Search Trees
• B-Trees: a search tree that allows internal nodes
to hold multiple values and have multiple
children
Balanced Search Trees
• B-Tree Variants: A popular variant of the B-Tree
is the 2-3-4 tree, where nodes are constrained to
hold 1, 2, or 3 values and therefore have 2, 3, or
4 subtrees (thus the name of the tree)
• Searching, insertion, and removal of information
is always O(log2N) and the amount of work
required to process the information within any
node is constant.
Balanced Search Trees
• A 2-3-4 tree can be implemented using a binary tree
structure called a red-black tree. In this structure nodes
are “colored” either red or black (each node must hold an
extra boolean value that indicates this information). The
color of a node indicates how its value fits into the
corresponding 2-3-4 tree.
• The Java Library includes two important classes that use
tree implementations. The Java TreeMap class supports
maps (see Chapter 8) and the Java TreeSet class
supports sets (see Section 5.7, “Collection Variations”).
In both cases the internal representation is a red-black
tree.
Balanced Search Trees
• AVL Trees: the
difference in height
between a node’s
two subtrees can be
at most 1.
• During addition and
removal of nodes
extra work is
required to ensure
the tree remains
balanced.
Extra: A Nonlinked Representation
of Binary Trees
• A binary tree can be stored in an array in such a way
that the relationships in the tree are not physically
represented by link members, but are implicit in the
algorithms that manipulate the tree stored in the array.
• We store the tree elements in the array, level by level,
left-to-right. If the number of nodes in the tree is
numElements, we can package the array and
numElements into an object
• The tree elements are stored with the root in
tree.nodes[0] and the last node in
tree.nodes[numElements – 1].
A Binary Tree and Its Array
Representation
Array Representation continued
• To implement the algorithms that manipulate the
tree, we must be able to find the left and right child
of a node in the tree:
– tree.nodes[index] left child is in tree.nodes[index*2 + 1]
– tree.nodes[index] right child is in tree.nodes[index*2 + 2]
• We can also can determine the location of its parent
node:
– tree.nodes[index]’s parent is in tree.nodes[(index – 1)/2].
• This representation works best, space wise, if the
tree is complete
Array Representation continued