# Binary Search Trees

Definition: A binary tree is a binary search tree iff it is empty or if the value of every node is both greater than or equal to every value in its left subtree and less than or equal to every value in its right subtree.

Note that a binary search tree is an ordered structure. That is, it maintains all of its elements in order.

One of many ways we can use a binary search tree is for sorting:

## Tree Sort

We can build a binary search tree and then do an inorder traversal. Since the cost of entering an element into a (balanced) binary tree of size n is log n, the cost of building the tree is

(log 1) + (log 2) + (log 3) + ... + (log n) = O(n log n) compares.

Traversal is O(n). Total cost is O(n log n) in both the best and average cases.

The worst case is if the list is in order, it then behaves more like an insertion sort, creating a tree with one long branch. This results in a tree search as bad as O(n2).

In the worst case, Heap Sort is better, since it automatically keeps the tree in balance. In the average case, Tree Sort is good -- it also has the side effect of building an ordered structure that is useful and interesting for other reasons.

Quicksort fastest on average - O(n log n)), but bad in worst case O(n2).
• Low overhead makes it perform well on average.
HeapSort takes O(n log n) in average and worst case.
• On random data somewhat slower than Quicksort and MergeSort. If you only need the first few items in a sorted list, it can be better since the initial heapify can be done in time O(n).
MergeSort takes O(n log n) in average and worst case, O(n) extra space.
• On random data somewhat slower than Quicksort.
• Performs well on external files where all data will not fit into memory.

## Binary Search Tree Implementation

Because it stores items in order, it is an implementation of OrderedStructure. We'll skip implementation of obvious methods, to focus on harder methods: add, get, and remove. We'll add protected methods locate, predecessor, and removeTop to make add, get, and remove easier.

```public class BinarySearchTree implements OrderedStructure
{
protected BinaryTreeNode root;
protected int count;

public BinarySearchTree()
// post: constructs an empty binary search tree.
{
root = null;
count = 0;
}

public boolean isEmpty()
// post: returns true iff binary search tree is empty.

public void clear()
// post: removes all elements from binary search tree

public int size()
// post: returns number of elements in binary search tree

protected BinaryTreeNode locate(BinaryTreeNode subRoot,
Comparable value)
// pre: subRootand value are non-null
// post: returned: 1 - existing tree node with the desired value, or
//                 2 - node to which value should be added.
{
Comparable subRootValue = (Comparable)subRoot.value();
BinaryTreeNode child;

// found at root: done
if (subRootValue.equals(value)) return subRoot;
// look left, if less, right if more
if (subRootValue.lessThan(value))
child = subRoot.right();
else
child = subRoot.left();
// if no child there: not in tree, return this node,
// else keep searching
if (child == null)
return subRoot;
else
return locate(child, value);
}

protected BinaryTreeNode predecessor(BinaryTreeNode root)
// pre: tree is not empty, root node has left child.
// post: returns pointer to predecessor of root
{
Assert.pre(root != null, "No predecessor to middle value.");
Assert.pre(root.left() != null, "Root has left child.");
BinaryTreeNode result = root.left();
while (result.right() != null)
result = result.right();
return result;
}

protected BinaryTreeNode successor(BinaryTreeNode root)
// pre: tree is not empty, root node has right child.
// post: returns pointer to successor of root
{
Assert.pre(root != null, "Tree is non-null.");
Assert.pre(root.left() != null,"Root has right child.");
BinaryTreeNode result = root.right();
while (result.left() != null)
result = result.left();
return result;
}

// post: adds a value to the binary search tree.
{
BinaryTreeNode newNode = new BinaryTreeNode(val);

// add value to binary search tree
// if there's no root, create value at root.
if (root == null)
root = newNode;
else {
Comparable value = (Comparable)val;
BinaryTreeNode insertLocation = locate(root,value);
Comparable nodeValue = (Comparable)insertLocation.value();
// Location returned is the successor or predecessor
// of the to-be-inserted value.
if (nodeValue.lessThan(value))
insertLocation.setRight(newNode);
else {
if (insertLocation.left() != null)
// if value is in tree, we insert just before
predecessor(insertLocation).setRight(newNode);
else
insertLocation.setLeft(newNode);
}
}
count++;
}

public boolean contains(Object val)
// post: returns true iff val is found within the tree
{
if (root == null) return false;

BinaryTreeNode possibleLocation = locate(root,(Comparable)val);
return val.equals(possibleLocation.value());
}

public Object get(Object val)
// post: returns object found in tree, or null
{
if (root == null) return null;

BinaryTreeNode possibleLocation = locate(root,(Comparable)val);
if (val.equals(possibleLocation.value()))
return possibleLocation.value();
else
return null;
}

public Object remove(Object val)
// post: removes one instance of val, if found
{
// remove value from a binary search tree
// no root, just quit
Comparable cval = (Comparable)val;

if (isEmpty()) return null;

if (val.equals(root.value())) // delete root value
{
BinaryTreeNode newroot = removeTop(root);
count--;
Object result = root.value();
root = newroot;
return result;
} else {
BinaryTreeNode location = locate(root,cval);

if (cval.equals(location.value())) {
count--;
BinaryTreeNode parent = location.parent();
if (parent.right() == location)
parent.setRight(removeTop(location));
else
parent.setLeft(removeTop(location));
return location.value();
}
}
return null;
}

protected BinaryTreeNode removeTop(BinaryTreeNode topNode)
// pre: tree is not empty.
// post: root of tree (topNode) is disconnected from tree
//       & new root is returned, new root has no parent.

{
// remove topmost BinaryTreeNode from binary search tree
BinaryTreeNode left  = topNode.left();
BinaryTreeNode right = topNode.right();
// disconnect top node
topNode.setLeft(null);
topNode.setRight(null);
// Case a, no left BinaryTreeNode
//   easy: right subtree is new tree
if (left == null)  return right;
// Case b, no right BinaryTreeNode
//   easy: left subtree is new tree
if (right == null) return left;
// Case c, left node has no right subtree
//   easy: make right subtree of left
BinaryTreeNode predecessor = left.right();
if (predecessor == null)
{
left.setRight(right);
return left;
}
// General case, slide down left tree
//   harder: predecessor of root becomes new root
//           parent always points to parent of n
BinaryTreeNode parent = left;
while (predecessor.right() != null)
{
parent = predecessor;
predecessor = predecessor.right();
}
// Assert: n is predecessor of root
parent.setRight(predecessor.left());
predecessor.setLeft(left);
predecessor.setRight(right);
return predecessor;
}

...
}
```
Complexity of add, get, contains, and remove all proportional to height of tree. If balanced, then O(log n), otherwise O(n) in worst case.