A tree is either null or a node followed by 0 or more trees
root is zero 1 + level(parent) sometimes, the level is defined to start at 1
A tree is a subset of a general connected structure called a graph. A node in a tree may have many children, but it has only one parent. A list is a tree of order 1.
struct node { char * value; node * left; node * mid; node * right; };
This tree is of order 3. This is like a linked list where each node can point to three other nodes instead of one. All we know about linked lists still applies although altered.
Going through a tree is called a traversal. This is a description of a path through each node in a tree. This is the same as the techniques we developed to look at every node in a list. Three common binary tree traversals are:
get class to suggest how to do these first.
In these routines, the function doroot() indicates the location in the code where we should put the statements or functions that are to be performed on the node we are looking at.
Examples
Traversal | Notation | Results |
preorder | prefix | + c * a b |
postorder | postfix (RPN) | c a b * + |
inorder | infix | c + a * b |
Discuss pre, post and inorder notation, and conversion stack actions to evaluate post instead of inorder
The code to implement a traversal looks like the description.
void print(node *t) { if(t == NULL) return(); print(t->left); cout << t->value; print(t->right); } // print
The routines above are recursive, it is possible to traverse a tree iteratively but is more difficult. Below is a routine that performs an inorder traversal of a tree without recursion.
inorder(T) ptr = T repeat while(ptr <> nil) push(ptr) ptr = ptr^.left endwhile if (not stack_empty()) then x = pop() print(x^.value) ptr = ptr^.right endif until (stack_empty() and (ptr = nil))
The algorithms that we will be discussing will concern a particular type of tree called a binary tree which is of order 2. And we are looking at a variant on binary trees that are ordered in the following way:
val1 val2 val3
In this tree, for every node, val2 < val1 < val3. This ordering is only a useful convention, it is not part of the definition of binary trees.
Example
struct node { char * value; node * left; node * right; };
Searching an ordered binary tree is like searching a linear list and like a binary search on an array. If the node we are looking at is not the one we want, then if the searched for value is less than the one we are looking at, it must be to the left of it and to the right if it is greater than the current node.
Example Ordered Binary Tree This tree results from inserting the nodes from data that is in this order: 8,7,15,3,10,18.
Search through this tree for 15 and then for 20. Note the value and location of T when the 20 is not found.
bool search(node * t, char * str) { if(t == NULL) return(false); int c = strcmp(str,t->value); if(c == 0) return(true); // found it if ( c > 0) return(search(t->right,str)); else return(search(t->left,str)); } // search
The algorithm presented above has the interesting property of leaving us at the location where a new node is to be inserted if it is not already in the tree. So a simple change will produce a search and insert algorithm.
bool insert(node * &t,char * str) { node * n; if(t == NULL) { if( (n = new node) == NULL) throw new Mem("insert: node"); if( (n->value = new char[strlen(str)+1]) == NULL) throw new Mem("insert: string"); (void) strcpy(n->value,str); n->left = n->right = NULL; t = n; return(true); } else { int c = strcmp(str,t->value); if(c == 0) { return(true); } else if ( c > 0) { return(insert(t->right,str)); } else { return(insert(t->left,str)); } } // t not NULL } // insert
Walk through what happens when an attempt is made to add a 20 to the tree above. Don't immediately connect the new node to the position in the 18 node. Let the class figure out why the connection is made.
The fact that T is passed in by reference is critical to this routine. When the new(T) is executed for node 20, the value (right hand value) of T is NULL. The location of T (left hand value) is the right child field of the 11 node.
The next routine can be used to print the tree on its side. It mostly illustrates the use of a second parameter to hold a value and count the number of levels of recursion.
void treeprint(node * t, int l) { if(t != NULL) { treeprint(t->right,l+1); for(int i=0; i< l; i++) cout << " "; cout << t->value << endl; treeprint(t->left,l+1); } } // treeprint
Deleting an element from an ordered binary tree is more difficult. There are several cases to deal with.