Lec#11
August 7 1995

class int_tree is designed to encapsulate a tree and allow an application
to store integers in the tree.  consider the .h file for the tree class:

class TNode {                      
  friend class int_tree;

  private:
      TNode(int k, TNode *l = 0, TNode *r = 0)    // constructor 
      : key(k),left(l),right(r) {}

      int key;  
      TNode *left;
      TNode *right; 
};
class int_tree
{
private:
  TNode * root;
  void delete_t(TNode*&); 
  int find_t(TNode*,int); 
  int insert_t(TNode*&,int); 
public:
 int_tree(){root=NULL;}
 ~int_tree() {delete_t(root);}
 int empty() {return root==NULL;}     
 int find(int val) {return find_t(root,val);}
 int insert(int val){return insert_t(root,val);}
};

Consider the above "find" function, it only has one parameter "val" that 
provides us with a value to search for.  If you recall the algorithm that
we covered in class for find was recursive and needed the root pointer as
a parameter.  How is that done? As you can see above, a find_t function 
has been written that has the root pointer as a paramter and the find 
function calls it.  find_t is in the private section so it can only be called
by the member functions and is not available to the application program.

============================================================================
lets discuss performance comparison for find and insert when data is 
stored in a linked list vs. a tree.  ttest.cc and ltest2.cc are designed
for that purpose.  ltest2.cc uses int_pointer_list class and ttest.cc uses
the int_tree class.

In each case, 10,000 elements are randomly generated and stored in the data
structure and cpu ticks are printed when each program is completely done.  I 
first just did an append for each of the elements in ltest2.cc and the same 
thing for ttest.cc. Here are cpu counts in each case:

ttest:		   930,000 
ltest2:		17,190,000

This basically means that it takes ltest2 approx. 20 times longer to
insert 10,000 elements than it does ttest.  why? Each append in 
int_pointer_list forces us to go through the whole list before a value is
appended to the list, whereas, in the tree's case, we only travel the
hight of the tree.  The cost for each append for linked list is O(n) and the
cost for each insert for the tree is O(log n).  this is despite the fact that
insert for trees is recursive and you pay for the recursion overhead.


I then changed both ttest.cc and ltest2.cc to first search for val before 
adding each element to the data structure, and only adding val, if it didn't 
exist already.  Here are the clock counts:

ttest:		 1,470,000
ltest2:	  	35,640,000

Is it possible to make append work more efficiently in the linked list case?

Is it possible to make insert work more efficiently in the tree case?


============================================================================
Lets discuss what makes an algorithm that seems naturally recursive, easyly
implementable non-recursively.

If an algorithm only follows a path in the tree the travels the hight of the
tree just once, then it can be easyly written non-recursively.

Lets write find non-recursively:

int int_tree::find_t(TNode * root,int val)
{ TNode * temp=root;
  while (temp != NULL)
  {
    if (temp->key == val) return 1;
    if (temp->key < val) 
      temp = temp->right;
    else
      temp = temp->left;
  }
  return 0;
}

===============================================================================
