Computer Science II Lab 6
                 Better Living through Better Trees

 Introduction
 ============
 Trees are wonderful.  Trees are the best thing ever!  Trees provide
 us with oxygen, paper, fruit, nuts, and they help us find websites
 with pictures of cats and/or people who are wrong and must be argued
 with (even though you don't know them and will never meet them in
 real life).  Ok, so there are at least two kinds of trees.  We will
 be working on creating the second one, the data structure which bears
 a resemblance to the biological one.  (If you want to make a
 biological tree, all you need is an acorn and time!)

 As we have discussed in lecture, trees are one of the most
 incredibly useful things in computer science.  Trees are what make
 it possible to cope with massive data sets and narrow them down
 quickly.  Binary trees, for example, can literally split the search
 space in half if they are organized correctly.  That is what this
 lab is about, implementing binary search trees so that we can
 achieve logarithmic run times when searching for data.

 In the lab that follows this one, we will use that tree to build an
 expression parser.  The lab which follows that one will further
 build on the concept by creating a small compiler/code generator
 stack!

 The key to these final 3 labs will be to take it nice and easy.
 There will be a lot of pointers flying all over the place, and there
 will be lots of complexity.  That's ok though.  We are building
 shapes in memory to manage that complexity.  So long as you follow
 the patterns, you won't get lost.  So take a deep breath and let's
 dive in!


 The Naive Binary Search Tree (Guided)
 =====================================
 Ok, so let's start off with a simple binary search tree.  Here, we
 are going to build a class which can contain a simple binary tree,
 and then we will write a bstTree insert function to go with it.
 Before I show you the tree template, I want to mention a few things
 about this tree.  The first is that it is fully linked, so each node
 connects to its parent.  The second is that it is not strictly in
 line with OOP goals.  The reason is that it passes pointers to
 subtrees back out to the user.  This is done largely because it
 makes the tree more flexible than it would be if we tried to use it
 with tree iterators.  We will be coding tree iterators later on, and
 will use this class as a building block for a more robust
 containers.  For now, know that we are trading OOP integrity for
 simplicity, but given that this is really about exploring
 algorithms, this should be ok.

 So without further ado, here is the binary search tree.  Your first
 task is, of course, to type out btree.h:

 --begin btree.h--
    1  /*
    2   * File:    btree.h
    3   * Purpose: A nice little binary tree template.
    4   */
    5  #ifndef BTREE_H
    6  #define BTREE_H
    7  #include <iostream>
    8
    9  template<typename T>
   10  class BinaryTree {
   11  public:
   12    //we always create tree nodes with data
   13    BinaryTree(T data) {
   14      left = right = parent;
   15      this->data = data;
   16    }
   17
   18    //destructor
   19    ~BinaryTree() {
   20      delete left;
   21      delete right;
   22    }
   23
   24    //link a left tree
   25    void linkLeft(BinaryTree<T> *node) {
   26      left = node;
   27      node->parent = this;
   28    }
   29
   30    //link a right tree
   31    void linkRight(BinaryTree<T> *node) {
   32      right = node;
   33      node->parent = this;
   34    }
   35
   36
   37    //get the left tree
   38    BinaryTree<T> *getLeft() {
   39      return left;
   40    }
   41
   42
   43    //get the right tree
   44    BinaryTree<T> *getRight() {
   45      return right;
   46    }
   47
   48
   49    //get the parent tree
   50    BinaryTree<T> *getParent() {
   51      return parent;
   52    }
   53
   54    //get the uncle
   55    BinaryTree<T> *getUncle() {
   56      BinaryTree<T> *gp;
   57
   58      //root nodes and non-grandchildren have no uncles
   59      if(!parent || !parent->parent)
   60        return NULL;
   61
   62      //get the grand parent
   63      gp = parent->parent;
   64
   65      //return the uncle
   66      if(parent == gp->left)
   67        return gp->right;
   68      else
   69        return gp->left;
   70    }
   71
   72
   73    //return the data itemm
   74    T getData() {
   75      return data;
   76    }
   77
   78
   79    //return the height of the tree
   80    int getHeight() {
   81      int lh=0, rh=0;
   82
   83      //get the heights of the left and the right
   84      if(left)
   85        lh = left->getHeight();
   86      if(right)
   87        rh = right->getHeight();
   88
   89      //1 + max of left and right height
   90      return 1 + (lh > rh ? lh : rh);
   91    }
   92
   93
   94    //print the tree
   95    void printTree() {
   96      int h = getHeight();
   97
   98      //print each level
   99      for(int i=1; i<=h; i++) {
  100        printLevel(i);
  101
  102        std::cout << std::endl;
  103      }
  104    }
  105
  106  private:
  107
  108    //print a level of the tree
  109    void printLevel(int h) {
  110      if(h == 1) {
  111        std::cout << data << "  ";
  112      } else {
  113        //either print what's on the left, or print a #
  114        if(left)
  115          left->printLevel(h-1);
  116        else
  117          std::cout << "#  ";
  118
  119        //either print what's on the right or print a #
  120        if(right)
  121          right->printLevel(h-1);
  122        else
  123          std::cout << "#  ";
  124      }
  125    }
  126
  127    T data;                 //The data element
  128    BinaryTree<T> *left;    //left child
  129    BinaryTree<T> *right;   //right child
  130    BinaryTree<T> *parent;  //parent
  131  };
  132
  133  #endif
 --end btree.h--

 Take a moment to study this design.  Notice how I still keep the
 three node pointers as private members.  This is so we can be
 assured that the parenting of the tree will be handled properly.
 You must use the get and the link methods to build the tree.

 Notice that I did not write traversals.  We'll be adding traversal
 iterators in the next lab.  I did, however, add a recursive print
 method.  When you run it, it will print the tree.  If a level of the
 tree has holes in it, it prints "#".  This function isn't the
 prettiest way to display the tree, but it should be easy enough to
 follow for debugging purposes.  If you want to modify the print
 functions so that it prints something more tree like, go for it.  In
 fact, I'll give you some extra credit points if you do!

 Also of note is the presence of the "getUncle" in addition to
 "getRight", "getLeft", and "getParent".  This returns a node's
 uncle, if it has one.  This won't be used in any of the labs in this
 class, but I have included it here so you can use this btree as a
 starting point for exploring other trees.  For instance, suppose you
 wanted to try your hand at creating a red-black tree.  If you do,
 you'll need to get the uncles.

 Ok, so now we have that little template, now we need to use it.
 Here is my test file, which contains the bstInsert function that
 works with this tree.

 --begin btreeTest.cpp--
    1  /*
    2   * This is a small test driver for our btree.  It builds a naive
    3   * BST.  Behold the glories of degenerative cases!
    4   */
    5
    6  #include <iostream>
    7  #include "btree.h"
    8
    9  using namespace std;
   10
   11  //an implementation of the bstInsert algorithm
   12  void bstInsert(BinaryTree<int> *t, int data) {
   13    if(data <= t->getData()) {
   14      //insert left
   15      if(!t->getLeft())
   16        t->linkLeft(new BinaryTree<int>(data));
   17      else
   18        bstInsert(t->getLeft(), data);
   19    } else {
   20      //insert right
   21      if(!t->getRight())
   22        t->linkRight(new BinaryTree<int>(data));
   23      else
   24        bstInsert(t->getRight(), data);
   25    }
   26  }
   27
   28
   29  int main(void) {
   30    BinaryTree<int> *t=NULL;
   31    int data;
   32
   33    do {
   34      //prompt the user
   35      cout << "Enter Number (-1 to exit): ";
   36
   37      //get the data
   38      cin >> data;
   39
   40      if(data == -1)
   41        continue;
   42
   43      if(!t)
   44        t=new BinaryTree<int>(data);
   45      else
   46        bstInsert(t, data);
   47    } while(data != -1);
   48
   49    //print the tree
   50    t->printTree();
   51
   52    return 0;
   53  }
 --end btreeTest.cpp--

 Enter this program and study it.  Make sure you can reconcile my
 implementation of bstInsert to the pseudocode you have in your
 notes.

 Now try the program out.  Here's a sample run through:

   Enter Number (-1 to exit): 10
   Enter Number (-1 to exit): 9
   Enter Number (-1 to exit): 7
   Enter Number (-1 to exit): 8
   Enter Number (-1 to exit): 13
   Enter Number (-1 to exit): 11
   Enter Number (-1 to exit): 14
   Enter Number (-1 to exit): -1
   10
   9  13
   7  #  11  14
   #  8  #  #  #  #  #

 So then, how do we read that tree output?  Well, the answer is that
 it is printing the tree in row order with a couple of spaces between
 the nodes.  So if you look at that, here's what you get if we adjust
 it so it is more tree like:
             10
        9         13
     7         11    14
       8

 If you squint at my function's output just right, you can see the
 tree.  If you adjust your program so that you print something more
 like the second example, I will give you some points.

 So now, we have this awesome little binary search tree, right?
 Wrong!  Consider this case:

   Enter Number (-1 to exit): 1
   Enter Number (-1 to exit): 2
   Enter Number (-1 to exit): 3
   Enter Number (-1 to exit): 4
   Enter Number (-1 to exit): -1
   1
   #  2
   #  #  3
   #  #  #  4

 As you can see, it rapidly degenerates into a linked list, so a
 search would just be an undesirable O(n) search, and not a super
 sweet O(lg n) search.

 So that's the best we can do without some kind of a balancing
 strategy, and that is where the guided portion leaves off.



 The AVL Tree (Challenge Lab)
 ============================
 As we discussed in class, the AVL tree is an auto-balancing binary
 search tree.  Your mission, should you choose to get a high grade,
 is to implement this soviet jewel.  How you may ask?  Well, I have
 given you tons of pseudocode in class, now is your chance to put it
 to good use.  To get you started, I have taken the liberty of
 designing a derived template based on the btree.  Here is the
 skeleton of that design.  I went ahead and filled in all the boring
 stuff (like comparison operators and all the stuff needed to make
 C++ happy about the inheritance structure), but I left all the juicy
 details to you.  I think the layout of my functions will give you a
 strong hint at what you need to do.  In fact if you look for the:

           /* to be done by intrepid students */

 comments, you will see what you need to fill in.  Note that I added
 2 new private members "rh" and "lh" which measure the height on each
 side.  You should maintain these as counters during the insertion
 process.  You should have some pseudocode from class to help with
 this.  So here is my skeleton code:

 --begin avltree.h--
    1  /*
    2   * An implementation of the AVL tree.
    3   */
    4
    5  #include "btree.h"
    6
    7  template<typename T>
    8  class AVLTree : public BinaryTree<T> {
    9  public:
   10    ///////////////////////////////////////
   11    // constructor
   12    ///////////////////////////////////////
   13    AVLTree(T data) : BinaryTree<T>(data) { lh = rh = 0; }
   14
   15
   16    ///////////////////////////////////////
   17    //operators
   18    ///////////////////////////////////////
   19    bool operator<(AVLTree<T> &rhs) {
   20      return this->getData() < rhs.getData();
   21    }
   22
   23    bool operator>(AVLTree<T> &rhs) {
   24      return this->getData() > rhs.getData();
   25    }
   26
   27    bool operator<=(AVLTree<T> &rhs) {
   28      return this->getData() <= rhs.getData();
   29    }
   30
   31    bool operator>=(AVLTree<T> &rhs) {
   32      return this->getData() >= rhs.getData();
   33    }
   34
   35    bool operator==(AVLTree<T> &rhs) {
   36      return this->getData() == rhs.getData();
   37    }
   38
   39
   40    ///////////////////////////////////////
   41    // wrappers (to avoid typecasts)
   42    ///////////////////////////////////////
   43    AVLTree<T> *getLeft() {
   44      return (AVLTree<T> *) BinaryTree<T>::getLeft();
   45    }
   46
   47    AVLTree<T> *getRight() {
   48      return (AVLTree<T> *) BinaryTree<T>::getRight();
   49    }
   50
   51    AVLTree<T> *getParent() {
   52      return (AVLTree<T> *) BinaryTree<T>::getParent();
   53    }
   54
   55    AVLTree<T> *getUncle() {
   56      return (AVLTree<T> *) BinaryTree<T>::getUncle();
   57    }
   58
   59    ///////////////////////////////////////
   60    //methods
   61    ///////////////////////////////////////
   62
   63    //inserts a new subtree, and returns the new avl root
   64    AVLTree<T> *insert(T data) {
   65      /* to be done by intrepid students */
   66    }
   67
   68
   69    //searchs for an item in an avl tree.  Returns the node pointer on
   70    //success, returns NULL if the item is not in the tree
   71    AVLTree<T> *search(T item) {
   72      /* to be done by intrepid students */
   73    }
   74
   75
   76  private:
   77    ///////////////////////////////////////
   78    //private methods
   79    ///////////////////////////////////////
   80
   81    //do the standard bst insert using a node
   82    void bstInsert(AVLTree<T> *node) {
   83      /* to be done by intrepid students */
   84    }
   85
   86    //rebalance the tree with root node
   87    void rebalance(AVLTree<T> *node) {
   88      /* to be done by intrepid students */
   89    }
   90
   91    //rotate the tree to the left
   92    void rotateLeft(AVLTree<T> *node) {
   93      /* to be done by intrepid students */
   94    }
   95
   96    //rotate the tree to the right
   97    void rotateRight(AVLTree<T> *node) {
   98      /* to be done by intrepid students */
   99    }
  100
  101    ///////////////////////////////////////
  102    //private data members
  103    ///////////////////////////////////////
  104    int lh;  //left tree height
  105    int rh;  //right tree height
  106  };
 --end avltree.h--

 To test your avl tree, rewrite the btreeTest.cpp file so that
 it uses your insert.  Also, add code to test the search function.
 So I should be able to build a tree, and then search for items.  If
 the item is not in the tree, it should tell me so.

 So in the end, you'll need to submit the following:
   - btree.h
   - avltree.h
   - btreeTest.cpp
   - avltreeTest.cpp

 HINTS:
   - Devise some way to test your tree rotations.  Get the rotations
     working before trying anything else.

   - You will establish links using the linkLeft and linkRight
     methods.  This will help ensure that you don't lose your parent
     links.

   - Review your pseudocode!  I've told you how to do most, if not
     all, of this stuff!

 Extra Credit
 ------------
 Notice anything missing?  That's right, there is no delete
 function.  Add it, and you'll get 10 points additional credit on the
 challenge portion of the lab!  My function signature for this is:

   void delete(T item);

 Happy hunting!