import structure.Comparable; public interface SortInterface { /** POST -- The elements are in non-decreasing order **/ public void sort (Comparable[] elts); }where
public interface Comparable { public boolean lessThan(Comparable item); // pre: item is non-null // post: returns true iff this object less than item }In order to test the sorts, we created a class of elements which met that specification. Here we create a special class called an ordered association (Association was defined in chapter 1 of Bailey).
public class Association { protected Object theKey; // key of the key-value pair protected Object theValue; // value of the key-value pair public Association(Object key, Object value) // pre: key is non-null. // post: constructs a key-value pair. { Assert.pre(key != null, "Key must not be null."); theKey = key; theValue = value; } public boolean equals(Object other) // pre: other is non-null Association // post: returns true iff the keys are equal { Association otherAssoc = (Association)other; return key().equals(otherAssoc.key()); } public Object value() // post: returns value from association { return theValue; } public Object key() // post: returns key from association { return theKey; } public void setValue(Object value) // post: sets association's value to value. { theValue = value; } public String toString() ... }Notice the type-cast that was needed in equals, because equals in Object takes a parameter of type Object, overridden method must take a parameter of the same type.
ComparableAssociation extends Association:
public class ComparableAssociation extends Association implements Comparable { public ComparableAssociation(Comparable key, Object value) // pre: key is non-null // post: constructs association of key and a value { super(key,value); } public boolean lessThan(Comparable other) // pre: other is non-null ComparableAssociation // post: returns true iff this key is less than other key { ComparableAssociation otherCA = (ComparableAssociation)other; Comparable thisKey = (Comparable)key(); Comparable otherKey = (Comparable)otherCA.key(); return thisKey.lessThan(otherKey); } public String toString() ... }
Note again how lessThan must perform a type cast on its parameter. Again the parameter must be of type Comparable since lessThan in Comparable does. (Isn't this annoying - a big part of my research in OO language design has been in eliminating this annoyance!).
Please read the rest of this chapter on your own. It discusses ordered vectors and ordered lists. You should understand the details of the implementations (see the code in the structures library as well as the material in the text).
In a linear structure, every element has unique successor.
In trees, may have many.
An edge connects a node to its subtrees.
The roots of the subtrees of a node are said to be the successors or descendants of the node.
There may be many nodes without any successors: These are called leaves. The others are called interior nodes.
All nodes except root have unique predecessor.
A collection of trees is called a forest.
Examples
A) binary search (or sort) tree
K, C, A, N, B, V, F, U, D, H, M
B) Expression trees
[A*(B-C)]+(D/~E)
Family tree terminology:
parent, child, sibling, ancestor, descendant
Node plus all of its descendents is called a subtree.
Simple path is series of distinct nodes such that there is an edge between successive nodes.
path length = number of nodes on path - 1 = # edges traversed in path,
height of node = length of longest path from that node to a leaf.
Height of tree = height of its root.
Depth of node is length of path from root to that node.
Degree of node is number of its direct descendents.
Level defined recursively:
Root is at level 0.
Level of any other node is one greater than level of its parent.
Level of a node also = length of path from root to that node.
We'll stick to binary trees for now! (All nodes of degree <= 2.)
Deal with oriented trees: Identify each subtree as being either left or right.
Lemma: If T is a binary tree, then at level k, T has <= 2k nodes.
Theorem: If T has height h then n = # nodes in T <= 2h+1 -1. Equivalently, if T has n nodes, then n - 1 >= h >= log(n+1) - 1.
Note that these bounds are tight.
A full binary tree of height h has all leaves on level h.
A complete binary tree of height h is obtained from a full binary tree of height h with 0 or more (but not all) of of the rightmost leaves at level h removed.
Say T is balanced if it has the minimum possible height for its # of nodes.
In this case, height = ceiling of log2(n+1) - 1 or O ( log2 n )
Note that a balanced tree may have lots of holes in it.
The structures package actually doesn't include an interface for binary trees (it just contains one class BinaryTree), but here is what it would look like if it existed:
public interface BinaryTreeInterface { public void clear(); // post: removes all nodes from tree public void insert(Object value); // pre: cursor is invalid // post: if tree empty, value is inserted at root, o'wise // value inserted where cursor last moved off tree public Object remove(); // pre: cursor is valid and has no children // post: leaf removed, cursor is moved to parent, if any public Object value(); // pre: cursor valid // post: returns value of object at cursor public void setValue(Object value); // pre: cursor valid // post: sets value found at cursor public void reset(); // post: moves the cursor to the root, if any public boolean valid(); // post: returns true if cursor points to a valid node. public boolean hasLeft(); // post: returns true iff cursor has left child public boolean hasRight(); // post: returns true iff cursor has right child public boolean hasParent(); // pre: cursor is valid // post: returns true iff cursor has parent public boolean isLeftChild(); // post: return true if cursor has parent & is left child public boolean isRightChild(); // post: return true if cursor has parent & is rt child public void moveLeft(); // pre: cursor is valid // post: cursor moves to left child of pre-cursor, // or off tree public void moveRight(); // pre: cursor is valid // post: cursor moves to right child of pre-cursor, // or off tree public void moveUp(); // pre: cursor is valid // post: cursor moves up to parent of pre-cursor public int height(); // post: returns height of cursor in tree // or -1 if tree is empty public int depth(); // post: returns depth of cursor in tree // or -1 if tree is empty public boolean isFull(); // post: return true iff subtree rooted at cursor is full public boolean isComplete(); // post: returns true iff subtree rooted at cursor is // complete public boolean isEmpty(); // post: returns true iff tree is emtpy public int size(); // post: returns number of nodes in tree public Iterator elements(); // post: returns inorder traversal of tree public Iterator inorderElements(); // post: returns inorder traversal of tree public Iterator preorderElements(); // post: returns preorder traversal of tree public Iterator postorderElements(); // post: returns postorder traversal of tree public String toString(); // post: returns string representation of tree }
Tree traversals - visit all nodes in a tree
root - left sub-tree - right sub-tree
left sub-tree - root - right sub-tree
left sub-tree - right sub-tree - root
Notice what happens when you do these traversals of expression trees: Get prefix, infix, or postfix notation.
Most algorithms involve two parts -
Once we have an expression tree, how can we evaluate it?
Evaluate left subtree, evaluate right subtree, then perform operation at root: