next up previous
Next: About this document Up: My Home Page

Trees

A tree is a connected, acyclic graph.

Trees are very useful structures for modeling relationships, such as family trees, corporate hierarchies, classification schemes, etc.

A leaf of a tree is a vertex of degree 1.

Claim: Any tree on n vertices has exactly n-1 edges.

Proof: By induction on the number of vertices. Clearly this is true on the only possible tree on 2 vertices.

Assume any tree on x < n vertices has x-1 edges. Now take any tree on n vertices. Because it is acyclic (without cycle) it must contain at least one leaf. Deleting this leaf (and its one edge) leaves a tree with n-1 vertices and n-2 edges, so there must be a total of n-1 edges in the tree! height6pt width4pt

.

Paths in Trees

Claim: If x and y are two vertices in a tree T, then there is unique simple path between x and y.

Proof: Since the tree is connected, there must be at least path between x and y.

If there were two or more paths between x and y, this would imply a cycle, and trees are acyclic. height6pt width4pt

Note that there are only two distinct (non-isomorphic) trees that can be drawn on 4 vertices.

How many distinct trees are there on five vertices? Six?

Trees and Recursion

Trees have a natural inductive structure that makes them extremely useful in algorithms.

(1) Delete a vertex of degree 1 from a tree, and you have a smaller tree.

(2) Delete any vertex from a tree and you have a set of smaller trees.

Rooted Binary Trees

A tree is rooted if we have designated a particular vertex as the root.

Family trees are rooted in the oldest ancestor.

A rooted tree T is either (1) empty, or (2) consists of a node called the root, together with a collection of ordered (from left to right) rooted trees tex2html_wrap_inline366 .

In a rooted tree, the order among ``brother'' nodes matters. Thus left is different from right. The five distinct binary trees with three nodes:

A binary tree is a rooted tree where each node has at most two descendants, the left child and the right child.

Let's Play 20 Questions!

1.

2.

3.

4.

5.

6.

7.

8.

9.

10.

11.

12.

13.

14.

15.

16.

17.

18.

19.

20.

Why Do Twenty Questions Suffice?

With one question, I can distinguish between two words: A and B; ``Is the key tex2html_wrap_inline372 ?''

With two questions, I can distinguish between four words: A,B,C,D; ``Is the tex2html_wrap_inline376 ?''

Each question I ask em doubles the number of words I can search in my dictionary.

tex2html_wrap_inline378 , which is much larger than any portable dictionary!

Thus I could waste my first two questions because tex2html_wrap_inline380 .

Binary Search Trees

A binary search tree is a binary tree where each node contains a key such that:

Binary Search Trees in ML

The datatype constructor in ML permits us to define new data types, like a BinaryTree:

- datatype 'a BinaryTree = btempty |
= bt of 'a * 'a BinaryTree * 'a BinaryTree ;
datatype 'a BinaryTree
con bt : 'a * 'a BinaryTree * 'a BinaryTree -> 'a BinaryTree
con btempty : 'a BinaryTree

Each BinaryTree consists of a header bt, a node value or key, and left / right subtrees, both of which must be BinaryTrees.

The special case of a tree with no keys in it, the empty tree, is denoted btempty.

- val Tree = bt(2,btempty,
= bt(3,btempty,
= bt(7,bt(6,bt(5,btempty,btempty),
= btempty),
= bt(8,btempty,btempty))
= )
= );
= = = = val Tree = bt (2,btempty,bt (3,btempty,bt #)) : int BinaryTree

The lookup Function

To look up an element in a binary search tree, we must test the label of the root against our desired key, and the recursively search the appropriate side's subtree:

- fun lookup (btempty,_) = false
= | lookup(bt(root:int,left,right),x:int) =
= if (x = root) then true
= else (if (x <= root) then lookup(left,x)
= else lookup(right,x) );
= = = = val lookup = fn : int BinaryTree * int -> bool

The search ends in failure whenever the subtree is btempty.

- lookup(Tree,6);
val it = true : bool
- lookup(Tree,1);
val it = false : bool
- lookup(Tree,9);
val it = false : bool
- lookup(Tree,8);
val it = true : bool
- lookup(btempty,6);
val it = false : bool

Expression Trees

Rooted trees can also be used to represent arithmetic formulas.

The root of the tree represents the final value of the computation.

The leaves represent the operands, and the intermediate nodes the operators.

This tree represents the equation (2+5)*(3+4) + (1*6) using conventional arithmetic notation, or tex2html_wrap_inline390 using reverse Polish notation (as on an HP calculator).

These formulas can be constructed by appropriately traversing the expression tree, i.e. visiting the nodes in the correct order.

There are three natural orders to visit the nodes of the tree, each of which walks up and down the tree in a recursive manner:

A fourth natural order, breadth-first traversal, traverses level-by-level, tex2html_wrap_inline392 .

This involves jumping around from one subtree to another, and hence is not good for evaluating expressions, although breadth-first traversal does have numerous applications in computer science.

``I think that I shall never see
a poem as lovely as a tree
Poems are wrote by fools like me but only G-d can make a tree ``
- Joyce Kilmer

Tree Traversal in ML

Note how only the order of the recursive calls changes in the three traversals.

fun inorder (bt_empty) = []
| inorder(bt(root:'a,left,right)) =
inorder(left) @ (root :: inorder(right));

fun preorder (bt_empty) = []
| preorder(bt(root:'a,left,right)) =
root :: (preorder(left) @ preorder(right));

fun postorder (bt_empty) = []
| postorder(bt(root:'a,left,right)) =
(postorder(left) @ postorder(right)) @ (root :: []);

- val Expression =
= bt("+",
- bt("*",
- bt("+",
- bt("2",bt_empty,bt_empty),
- bt("5",bt_empty,bt_empty) ),
- bt("*",
- bt("3",bt_empty,bt_empty),
- bt("4",bt_empty,bt_empty) ) ),
- bt("*",
- bt("1",bt_empty,bt_empty),
- bt("6",bt_empty,bt_empty) ) );
= = = = = = = = = val Expression = bt ("+",bt ("*",bt #,bt #),bt ("*",bt #,bt #)) : string BinaryTree

- inorder(Expression);
val it = ["2","+","5","*","3","*","4","+","1","*","6"]
-
- preorder(Expression);
val it = ["+","*","+","2","5","*","3","4","*","1","6"]
-
- postorder(Expression);
val it = ["2","5","+","3","4","*","*","1","6","*","+"]

These traversal functions could be easily modified to compute the value of the arithmetic tree instead of just returning the formula.

Inorder Traversals of Binary Search Trees

Why is an in-order traversal called in order?

Note that an in-order traversal of a binary search tree lists all the keys in alphabetical order:

- inorder(Tree);
val it = [2,3,5,6,7,8] : int list




next up previous
Next: About this document Up: My Home Page

Steve Skiena
Tue Aug 24 22:59:50 EDT 1999