Fundamentals of Computer Science II (CSC-152 97F)

[News] [Basics] [Syllabus] [Outlines] [Assignments] [Examples] [Readings] [Bailey Docs]

# Outline of Class 38: Trees

## Miscelleneous

• We'll spend a little time on assignment six, assignment seven, and assignment eight.
• If you'll need more time on these assignments, please let me know.
• I will be in tonight (Wednesday) from about 7:30 to 10:00pm, and most of the day tomorrow (Thursday). Feel free to stop in and ask questions. I'll be working on a book chapter, but am still happy to speak with you. However, I may give rather short answers.
• Unfortunately, grading has been delayed by this book chapter.

## Introduction to Trees

• Many of the structures we've seen up to this point are linear in that they are logically represented as a straight line of elements.
• By modifying one or more of the restrictions on our structures, we can come up with some interesting nonlinear structures.
• Sometimes, modifications to one restriction may lead to modifications to other restrictions.
• Consider lists. In one sense, lists are structures with the following restrictions:
• Lists are collections of elements.
• There are two special elements, first and last.
• Every element except for the last element has exactly one next element.
• The last element has zero next elements.
• Every element except for the first element has exactly one previous element.
• The first element has zero previous elements.
• Suppose we allowed each element to have at least one next element (but maintain the restriction on previous elements).
• We'd also need to permit multiple "last" elements (o/w it's useless to have multiple next elements).
• Trees are
• Collections of nodes (elements) with
• A designated root (first element)
• A number of leaves (last elements)
• In which every node except the leaves have one or more children (next elements)
• The leaves have zero children.
• All nodes except the root have exactly one parent (previous element).
• The root has no parents.
• Ordered Trees number the children of each node.
• What are the alternatives?
• Binary Trees are ordered trees in which nodes have at most two children. They are among the most common types of trees.
• Trees are used for a wide variety of purposes in computing (and also in life -- consider family trees), including
• building efficient ordered structures
• representing arithmetic expressions
• representing programs in an easily manipulable way
• representing relationships between classes in Java
• ...
• Traditionally, there is a value associated with each node in the tree.
• Depending on the usage, one may or may not choose to associate values with all nodes.
• The most common alternative is to only store values at the leaves.

### Interfaces for Trees

• While we have a high-level view of trees, it is also important to ground that understanding in a particular interface that would be provided by a tree structure.
• As with all of our other structures, there is some difference of opinion as to what should be provided in an interface.
• Here is a very simple tree interface using a `TreeNode` class.
• `TreeNode(Object value)` -- Create a new tree.
• `void setChild(int childnum, TreeNode child)` -- Set a child (subtree) of the current tree.
• `TreeNode getChild(int childnum)` -- Get one of the subtrees of the current tree.
• `Object value()` -- Get the value at the root of the tree.
• Note that there is no deletion operation. Why not?
• In this interface, it's because there is no purpose to deletion -- we can simply stop referring to a node.
• In more complex interfaces, it may be because it's not immediately clear what deletion should do (except at the leaves).
• Should it delete a node and all descendants?
• Should it delete just the node and make its children the children of its parent?
• Should it delete just the node and somehow manipulate its children?
• Note also that there is no parent operation in this interface. Why not? Consider it analagous to our singly-linked lists in which there was a next operation, but not a previous operation.

### An Application: Expression Trees

• One convenient use of trees is representing mathematical expressions.
• As we've seen in the past, standard infix notation is hard to read and understand.
• Just because an operator appears between two numbers, it doesn't mean that the operator should be applied to the two numbers. Consider the `+` in `2+3*5`.
• Parenthesization is often needed to enforce alternate orders of evaluation.
• There are a number of solutions to this problem, but tree representation is one particularly nice one.
• In the tree representation of an expression, the operation to be applied is at the root of the tree and the arguments (which may also be expressions) are subtrees.
• For example, the tree corresponding to `2+3*5` has
• `+` at the root,
• `2` as the left subtree,
• `*` as the root of the right subtree,
• `3` as the left subtree of the right subtree, and
• `5` as the right subtree of the right subtree.
• Visually,
```     +
/ \
2   *
/ \
3   5
```
• We might build this tree with: operation with
```ExpressionTree two = new ExpressionTree(2);
ExpressionTree three = new ExpressionTree(3);
ExpressionTree five = new ExpressionTree(5);
ExpressionTree right =
new ExpressionTree(Operation.MULTIPLY, three, five);
ExpressionTree whole =
new ExpressionTree(Operation.ADD, two, right);
```
• This assumes we've created a variety of constructors. We might instead use something like the following for the final "step" above.
```ExpressionTree whole = new ExpressionTree();
whole.setChild(0, two);
whole.setChild(1, right);
```

#### Evaluating Expression Trees

• How do you evaluate expression trees?
• For the base cases, you return the evaluate stored in the node.
• For the other cases, you evaluate the subtrees and then apply the operation to those evaluates.
• Here's some sample code
```  /** Get the evaluate of the current expression. */
public int evaluate() throws Exception {
if (type == INTEGER) {
return ((Integer) getRoot()).intValue();
} // INTEGER
else if (type == VARIABLE) {
return lookupVariable(getRoot());
} // VARIABLE
else if (type == OPERATION) {
// Evaluate arguments
int[] args = new int(arity());
for (int i = 0; i < arity(); ++i) {
args[i] = getChild(i).evaluate();
} // for
// Apply the operation
return ((Operation) getRoot()).apply(args);
} // OPERATION
else {
throw new Exception("Invalid expression tree");
}
} // evaluate()
```
• Note that this assumes that there is a separate `Operation` class which supports an `apply(int[])` method. That method might look something like:
```public int apply(int[] args) throws MathException {
if (op == ADD) {
if (args.length != 2) {
throw new MathException("Add requires two arguments");
}
return args + args;
}
else if (op == MULTIPLY) {
if (args.length != 2) {
throw new MathException("Multiply requires two arguments");
}
return args * args;
}
else if (op == NEGATE) {
if (args.length != 1) {
throw new MathException("Negation requires one argument");
}
return -args;
}
...
} // apply
```

Outlines: prev next

[News] [Basics] [Syllabus] [Outlines] [Assignments] [Examples] [Readings] [Bailey Docs]

Disclaimer Often, these pages were created "on the fly" with little, if any, proofreading. Any or all of the information on the pages may be incorrect. Please contact me if you notice errors.

Source text last modified Wed Nov 12 12:18:48 1997.

This page generated on Wed Nov 12 12:20:30 1997 by SiteWeaver.

Contact our webmaster at rebelsky@math.grin.edu