12a: Binary Search Trees

ICS211, Spring 2013
Dr. Zach

(switch view)

Status

Building a sample tree

public static void main(String[] args) {
  TreeNode<String> leftTree = new TreeNode<String>("B",
      new TreeNode<String>("D"), new TreeNode<String>("E"));
  TreeNode<String> rightTree = new TreeNode<String>("C",
      new TreeNode<String>("F"), new TreeNode<String>("G"));
  TreeNode<String> root = new TreeNode<String>("A", leftTree, rightTree);

  //now what?
}

A diagram of the tree constructed by the code above.

Tree Traversals

In-order Traversal

//note: overly simplistic -- no spacers between elements
public static <E> String toStringInOrder(TreeNode<E> n) {
  if (n == null) {
    return "";
  }else {
    String str = "";
    str += toStringInOrder(n.getLeft());
    str += n.getData();
    str += toStringInOrder(n.getRight());
    return str;
  }
}

In main:

  //now what?  
  System.out.println(toStringInOrder(root));  //prints?  DBEAFCG

Pre-order Traversal

//note: overly simplistic -- no spacers between elements
public static <E> String toStringPreOrder(TreeNode<E> n) {
  if (n == null) {
    return "";
  }else {
    String str = "";
    str += n.getData();
    str += toStringPreOrder(n.getLeft());
    str += toStringPreOrder(n.getRight());
    return str;
  }
}

In main:

  //now what?  
  System.out.println(toStringPreOrder(root));  //prints?  ABDECFG

Post-order Traversal

//note: overly simplistic -- no spacers between elements
public static <E> String toStringPostOrder(TreeNode<E> n) {
  if (n == null) {
    return "";
  }else {
    String str = "";
    str += toStringPostOrder(n.getLeft());
    str += toStringPostOrder(n.getRight());
    str += n.getData();
    return str;
  }
}

In main:

  //now what?  
  System.out.println(toStringPostOrder(root));  //prints?  DEBFGCA

ADT: Binary Search Tree

BST: Constructor

public class BinarySearchTree<E extends Comparable<E>> {

  private TreeNode<E> root = null;
  private int size = 0;

  /** Creates an empty tree. */
  public BinarySearchTree() {
  }

BST: Add

  /** Adds the given item to this BST. */
  public void add(E item) {
    this.size++;
    if (this.root == null) {
      //tree is empty, so just add item
      this.root = new TreeNode<E>(item);
    }else {
      //find where to insert, with pointer to parent node
      TreeNode<E> parent = null;
      TreeNode<E> curr = this.root;
      boolean wentLeft = true;
      while (curr != null) {  //will execute at least once
        parent = curr;
        if (item.compareTo(curr.getData()) <= 0) {
          curr = curr.getLeft();
          wentLeft = true;
        }else {
          curr = curr.getRight();
          wentLeft = false;
        }
      }

      //now add new node on appropriate side of parent
      curr = new TreeNode<E>(item);
      if (wentLeft) {
        parent.setLeft(curr);
      }else {
        parent.setRight(curr);
      }
    }
  }

Iterative version. Rather long.

BST: Add (recursive version)

public void add(E item) {
  this.root = add(item, root);
  this.size++;
}

private TreeNode<E> add(E item, TreeNode<E> subtree) {
  if (subtree == null) {
    return new TreeNode<E>(item);
  }else {
    if (item.compareTo(subtree.getData()) <= 0) {
      subtree.setLeft(add(item, subtree.getLeft()));
    }else {
      subtree.setRight(add(item, subtree.getRight()));
    }
    return subtree;
  }
}

BST: toString

  @Override
  public String toString() {
    return toString(this.root);
  }

  private String toString(TreeNode<E> n) {
    if (n == null) {
      return " ";
    }else {
      String str = "[";
      str += toString(n.getLeft());
      str += n.getData();
      str += toString(n.getRight());
      str += "]";
      return str;
    }
  }

Multi-line output would be easier to read, but this clearly shows in-order traversal logic.

BST: Time to test...

  public static void main(String[] args) {
    BinarySearchTree<String> bst = new BinarySearchTree<String>();
    System.out.println(bst);  //prints: " "
    bst.add("G");
    System.out.println(bst);  //prints: [ G ]
    bst.add("D");
    System.out.println(bst);  //prints: [[ D ]G ]
    bst.add("B");
    bst.add("C");
    bst.add("L");
    bst.add("M");
    bst.add("H");
    System.out.println(bst);  //prints: [[[ B[ C ]]D ]G[[ H ]L[ M ]]]
  }

BST: Size

  /** Returns the number of elements currently in this BST. */
  public int size() {
    return this.size;    
  }

BST: Get

  /**
   * Returns first item from tree that is equivalent (according to compareTo)
   * to the given item.  If item is not in tree, returns null.
   */
  public E get(E item) {
    return get(item, this.root);
  }

  /** Finds it in the subtree rooted at the given node. */  
  private E get(E item, TreeNode<E> node) {
    if (node == null) {
      return null;
    }else if (item.compareTo(node.getData()) < 0) {
      return get(item, node.getLeft());
    }else if (item.compareTo(node.getData()) > 0) {
      return get(item, node.getRight());
    }else {
      //found it!
      return node.getData();
    }
  }

Or similar code for a boolean contains(E item) method.

BST: Remove... planning.

BST: findMax

  /** Returns the greatest value (right-most node) of the given subtree. */
  private E findMax(TreeNode<E> n) {
    if (n == null) {
      return null; //error case: no tree
    }else if (n.getRight() == null) {
      //can't go right any more, so this is max value
      return n.getData();
    }else {
      return findMax(n.getRight());
    }
  }

BST: Remove

  /**
   * Removes the first equivalent item found in the tree.
   * If item does not exist to be removed, throws IllegalArgumentException().
   */
  public void remove(E item) {
    this.root = remove(item, this.root);
  }

  private TreeNode<E> remove(E item, TreeNode<E> node) {
    if (node == null) {
      //didn't find item
      throw new IllegalArgumentException(item + " not found in tree.");
    }else if (item.compareTo(node.getData()) < 0) {
      //go to left, saving resulting changes made to left tree
      node.setLeft(remove(item, node.getLeft()));
      return node;
    }else if (item.compareTo(node.getData()) > 0) {
      //go to right, saving any resulting changes
      node.setRight(remove(item, node.getRight()));
      return node;
    }else {
      //found node to be removed!
      if (node.getLeft() == null && node.getRight() == null) {
        //leaf node
        return null;
      }else if (node.getRight() == null) {
        //has only a left child
        return node.getLeft();
      }else if (node.getLeft() == null) {
        //has only a right child
        return node.getRight();
      }else {
        //two children, so replace the contents of this node with max of left tree
        E max = findMax(node.getLeft());  //get max value
        node.setLeft(remove(max, node.getLeft())); //and remove its node from tree
        node.setData(max);
        return node;
      }
    }
  }

Full BST class so far

For next time...