CS 136 - Lecture 28

  1. Binary Search Trees (cont'd)
    1. Binary Search Tree Implementation

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;
    }

    public void add(Object val)
    // 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.