DATA STRUCTURES AND ALGORITHMS CS 3139
Lecture:  February 11th, 1999







ANNOUNCEMENTS

Please be sure to regularly check web page for updates and changes.  We live at two places!
http://www.cs.columbia.edu/~nieh/classes/w3139_s99
http://www.cs.columbia.edu/~w3139-02
 
 

REVIEW

Queue Implementation

Lists Trees Binary Search Trees

TODAY

Binary search trees:  Implementation and analysis
AVL trees
 
 

BINARY SEARCH TREES

Removing elements from a BST is a little tricky.  We have three cases to consider:

 We went over most of this code in class.  Be sure that you thoroughly understand everything that follows.
 
    class BinaryNode
    {
            // Constructors
        BinaryNode( Comparable theElement )
        {
            this( theElement, null, null );
        }

        BinaryNode( Comparable theElement, BinaryNode lt, BinaryNode rt )
        {
            element  = theElement;
            left     = lt;
            right    = rt;
        }

            // Friendly data; accessible by other package routines
        Comparable element;      // The data in the node
        BinaryNode left;         // Left child
        BinaryNode right;        // Right child
    }
 

    // BinarySearchTree class
    //
    // CONSTRUCTION: with no initializer
    //
    // ******************PUBLIC OPERATIONS*********************
    // void insert( x )       --> Insert x
    // void remove( x )       --> Remove x
    // Comparable find( x )   --> Return item that matches x
    // Comparable findMin( )  --> Return smallest item
    // Comparable findMax( )  --> Return largest item
    // boolean isEmpty( )     --> Return true if empty; else false
    // void makeEmpty( )      --> Remove all items
    // void printTree( )      --> Print tree in sorted order

    /**
     * Implements an unbalanced binary search tree.
     * Note that all "matching" is based on the compareTo method.
     * @author Mark Allen Weiss
     */
    public class BinarySearchTree
    {
        /**
         * Construct the tree.
         */
        public BinarySearchTree( )
        {
            root = null;
        }

        /**
         * Insert into the tree; duplicates are ignored.
         * @param x the item to insert.
         */
        public void insert( Comparable x )
        {
            root = insert( x, root );
        }

        /**
         * Remove from the tree. Nothing is done if x is not found.
         * @param x the item to remove.
         */
        public void remove( Comparable x )
        {
            root = remove( x, root );
        }

        /**
         * Find the smallest item in the tree.
         * @return smallest item or null if empty.
         */
        public Comparable findMin( )
        {
            return elementAt( findMin( root ) );
        }

        /**
         * Find the largest item in the tree.
         * @return the largest item of null if empty.
         */
        public Comparable findMax( )
        {
            return elementAt( findMax( root ) );
        }

        /**
         * Find an item in the tree.
         * @param x the item to search for.
         * @return the matching item or null if not found.
         */
        public Comparable find( Comparable x )
        {
            return elementAt( find( x, root ) );
        }

        /**
         * Make the tree logically empty.
         */
        public void makeEmpty( )
        {
            root = null;
        }

        /**
         * Test if the tree is logically empty.
         * @return true if empty, false otherwise.
         */
        public boolean isEmpty( )
        {
            return root == null;
        }

        /**
         * Print the tree contents in sorted order.
         */
        public void printTree( )
        {
            if( isEmpty( ) )
                System.out.println( "Empty tree" );
            else
                printTree( root );
        }

        /**
         * Internal method to get element field.
         * @param t the node.
         * @return the element field or null if t is null.
         */
        private Comparable elementAt( BinaryNode t )
        {
            return t == null ? null : t.element;
        }

        /**
         * Internal method to insert into a subtree.
         * @param x the item to insert.
         * @param t the node that roots the tree.
         * @return the new root.
         */
        private BinaryNode insert( Comparable x, BinaryNode t )
        {
/* 1*/      if( t == null )
/* 2*/          t = new BinaryNode( x, null, null );
/* 3*/      else if( x.compareTo( t.element ) < 0 )
/* 4*/          t.left = insert( x, t.left );
/* 5*/      else if( x.compareTo( t.element ) > 0 )
/* 6*/          t.right = insert( x, t.right );
/* 7*/      else
/* 8*/          ;  // Duplicate; do nothing
/* 9*/      return t;
        }

        /**
         * Internal method to remove from a subtree.
         * @param x the item to remove.
         * @param t the node that roots the tree.
         * @return the new root.
         */
        private BinaryNode remove( Comparable x, BinaryNode t )
        {
            if( t == null )
                return t;   // Item not found; do nothing
            if( x.compareTo( t.element ) < 0 )
                t.left = remove( x, t.left );
            else if( x.compareTo( t.element ) > 0 )
                t.right = remove( x, t.right );
            else if( t.left != null && t.right != null ) // Two children
            {
                t.element = findMin( t.right ).element;
                t.right = remove( t.element, t.right );
            }
            else
                t = ( t.left != null ) ? t.left : t.right;
            return t;
        }

        /**
         * Internal method to find the smallest item in a subtree.
         * @param t the node that roots the tree.
         * @return node containing the smallest item.
         */
        private BinaryNode findMin( BinaryNode t )
        {
            if( t == null )
                return null;
            else if( t.left == null )
                return t;
            return findMin( t.left );
        }

        /**
         * Internal method to find the largest item in a subtree.
         * @param t the node that roots the tree.
         * @return node containing the largest item.
         */
        private BinaryNode findMax( BinaryNode t )
        {
            if( t != null )
                while( t.right != null )
                    t = t.right;

            return t;
        }

        /**
         * Internal method to find an item in a subtree.
         * @param x is item to search for.
         * @param t the node that roots the tree.
         * @return node containing the matched item.
         */
        private BinaryNode find( Comparable x, BinaryNode t )
        {
            if( t == null )
                return null;
            if( x.compareTo( t.element ) < 0 )
                return find( x, t.left );
            else if( x.compareTo( t.element ) > 0 )
                return find( x, t.right );
            else
                return t;    // Match
        }

        /**
         * Internal method to print a subtree in sorted order.
         * @param t the node that roots the tree.
         */
        private void printTree( BinaryNode t )
        {
            if( t != null )
            {
                printTree( t.left );
                System.out.println( t.element );
                printTree( t.right );
            }
        }
 
 

ANALYSIS

We expect tree operations to take O(log N) where N = number of nodes

Alternatively, the running time of tree operations can also be expressed as O(d) where d is the depth of the target node.

The question is, does d = log N?  It all depends on balance.
 
 

AVL TREES

We need to balance trees to get good performance.  Balancing can be expensive though.  Can we balance in O(log N) time?  YES!  AVL trees!

Possible Balancing Criterion

Avl Trees Rebalancing AVLs Lets say a node W needs to be rebalanced.  W's two subtrees have heights differing by a factor of 2.  There are 4 cases we must consider:
  1. Insertion into L subtree of L child of W
  2. Insertion into R subtree of L child of W
  3. Insertion into L subtree of R child of W
  4. Insertion into R subtree of R child of W
Cases 1 and 4 are mirror images (insertion on outside):  Requires single rotation of tree
Cases 2 and 3 are mirror images (insertion on inside):    Requires double rotation of tree

In class, we went through several examples of how single and double rotation in AVL trees is implemented.  Please refer for section 4.4
of the text for a good overview of these techniques.

Sample Problem

Sequence:  3 2 1 4 5 6
Given the above sequence of inputs, construct the appropriate AVL tree.
 
 
 

  Nhat Minh Dau, nmd13@columbia.edu