Return to the Lecture Notes Index

15-200 Lecture 25 (Friday, March, 2005)

Deleting from a Binary Search Tree

What is it that makes a Binary Search Tree what it is? Of course, it is the fact that all nodes to the left of node a will be less than a, and all nodes to the right of a will be greater. Adding nodes to a BST is easy: all you have to do is traverse down the tree until find a spot where you can add it safely, and then add.

But what about deleting? The above fact about BST's is what makes deleting difficult.

A Binary Search Tree becomes completely useless if it loses it's order property that is described above. What is it about deleting that might cause this property to be in danger?

Deleting a leaf is the most trivial of deletes. All you need to do is set the reference to that particular node to null, because there are no nodes under it, and you don't have to worry about restructuring the tree. Of course, the reference to the node you want to delete will lie in its parent! So how can you go about doing this? The solution lies in recursion, and thats where we're headed now.

So deleting seems pretty easy when deleting a leaf, but what about when you want to delete the root of the tree? Let's take a quick look at a common situation.

             10
            /  \
           /    \
          7     15
         / \   /  \
        /   \ 12  18
       5     9
            /
           8
  

So here we want to delete the root of the tree, which is 10. What would we make the root? The tree, before the delete, represents the list

  5, 7, 8, 9, 10, 12, 15, 18
  

Notice that the root, 10, divides the subtress rooted at 5 and at 15. So, if we delete 10, we must replace it with a number that will divide these two subtrees -- either 9 or 12.

To select these, we look for the right-most item in the left subtree, which is 9, or the left-most itme in the right subtree, which is 12.

The right-most item in the left subtree can be found by "going right until we can't go right anymore" in the left tree. Similarly, the left-most item in the right subtree can be found by "going left until we can't go left anymore". It is important to realize that these traversals never change direction -- always left, or always right. Changing direction would move us away from the extreme end of the list, whcih is the middle of the whole tree.

Once we find the right item, we copy it into the hole created by the deletion, and then recursively call delete on it. That's how we fill the hole created by its own deletion.

  public void delete (Comparable c)
{
       root = delete(root, c);
}

/*
 * A recursive method that will copy the correct data into the node to
 * be deleted, and then call delete on the node where it got that data.
 * The method will continue to go recursively, until it is called to delete
 * a leaf, in which case it merely makes the reference to that node null.
 */
private static BinaryNode delete (BinaryNode bn, Comparable c)
{
        if (null == c) return bn;

        if (null == bn) return null;

        if (c.compareTo(bn.getData()) == 0)
        {
                if (bn.isLeaf()) return null;

                if (bn.getLeft() == null) return bn.getRight();

                if (bn.getRight() == null) return bn.getLeft();

                Comparable replacementData = getRightmost(bn.getLeft()).getData();

                return new BinaryNode (/* data */ replacementData,
                                       /* left */ delete(bn.getLeft(), replacementData),
                                       /* right */ bn.getRight());
        }

        else if (data.compareTo(bn.getData()) < 0)
        {
                bn.setLeft(delete(bn.getLeft(), c));
                return bn;
        }

        else
        {
                bn.setRight(delete(bn.getRight(), c));
                return bn;
        }
}

/*
 * The method to find the rightmost node in the left subtree.
 * It is used to find the proper data to put in the node to be deleted.
 */
private BinaryNode getRightmost(BinaryNode bn)
{
        // Special case: empty tree
        if (null == bn)
                return null;

        // Special case: no right child
        if (null == bn.getRight())
                return bn;

        // Common case: Go right
        return getRightmost(bn.getRight());
}