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
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:
Game is held as tree with answers at leaves and questions as internal nodes.
Asking questions corresponds to path from root to a leaf, where leaf holds animal which is best guess.
If guess wrong, then add new animal and corresponding question (w/old animal, too) in tree in place of old animal.
Game is much more interesting if save old games in file so can build tree again.
Discuss how next class.
public class BinaryTreeNode { protected Object val; protected BinaryTreeNode parent; protected BinaryTreeNode left; protected BinaryTreeNode right; public BinaryTreeNode(Object value, BinaryTreeNode left, BinaryTreeNode right) // post: returns a node referencing value & subtrees { val = value; setLeft(left); setRight(right); } public BinaryTreeNode left() // post: returns reference to left subtree, or null public BinaryTreeNode right() // post: returns reference to right subtree, or null public BinaryTreeNode parent() // post: returns reference to parent node, or null public void setLeft(BinaryTreeNode newLeft) // post: sets left subtree to newLeft // reparents newLeft if not null { if (left != null && (left.parent() == this)) left.setParent(null); left = newLeft; if (left != null) left.setParent(this); } public void setRight(BinaryTreeNode newRight) protected void setParent(BinaryTreeNode newParent) // post: reparents this node to parent reference, or null public static int size(BinaryTreeNode n) // post: returns the size of the subtree rooted at n public static BinaryTreeNode root(BinaryTreeNode n) // post: returns the root of the tree containing node n { if ((n == null) || (n.parent() == null)) return n; else return root(n.parent()); } public static int height(BinaryTreeNode n) // post: returns the height of a node n in its tree { if (n == null) return -1; return 1 + Math.max(height(n.left()),height(n.right())); } public static int depth(BinaryTreeNode n) // post: returns the depth of a node in the tree { if (n == null) return -1; return 1 + depth(n.parent()); } ... public boolean isLeftChild() // post: returns true if this is a left child of parent. { if (parent() == null) return false; return this == parent().left(); } public Object value() // post: returns value associated with this node. public void setValue(Object value) // post: sets the value associated with this node }
Come back and talk about iterators later. Nothing really surprising above.
Notice must be careful in setting element to left or right (must connect both ways) - extra test at beginning setting parent field of elt currently there to null isn't really necessary.
Notice root, height, and depth are all recursive (and are all static - i.e., belong with class, not object!).