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
}
Try to 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!).
BinaryTree class relatively straightforward, but watch cursor:
public class BinaryTree {
protected BinaryTreeNode root; // root of the tree
protected BinaryTreeNode cursor; // ptr to current node
protected BinaryTreeNode prior; // cursor's prior value
// cursor result of moving left
protected boolean wentLeft;
protected int size; // the size of the tree
public BinaryTree()
// post: creates an empty binary tree
{
clear();
}
public void clear()
// post: removes all nodes from tree
{
root = cursor = null;
size = 0;
}
public void insert(Object value)
// pre: cursor is invalid
// post: if tree empty, value inserted at root, otherwise
// value inserted where cursor last moved off tree
{
Assert.pre(cursor == null, "Insertion does not overwrite value.");
if (prior == null) {
Assert.pre(root == null, "Insertion at root only in empty tree.");
cursor = root = new BinaryTreeNode(value);
} else {
if (wentLeft) {
prior.setLeft(cursor = new BinaryTreeNode(value));
} else {
prior.setRight(cursor = new BinaryTreeNode(value));
}
}
size++;
}
public Object remove()
// pre: cursor is valid and has no children
// post: leaf is removed, cursor moved to parent, if any
{
Assert.pre(cursor != null,"Node to be removed exists.");
Assert.pre(!(hasLeft()||hasRight()), "Node to be removed is leaf.");
Object value = cursor.value();
if (isLeftChild()) {
moveUp();
cursor.setLeft(null);
} else if (isRightChild()) {
moveUp();
cursor.setRight(null);
} else {
root = cursor = prior = null;
}
size--;
return value;
}
public Object value()
// pre: cursor valid
// post: returns value of object at cursor
{
return cursor.value();
}
public void setValue(Object value)
// pre: cursor valid
// post: sets value found at cursor
{
cursor.setValue(value);
}
public void reset()
// post: moves the cursor to the root, if any
{
cursor = root;
prior = null;
}
public boolean valid()
// post: returns true if cursor points to a valid node.
{
// be aware that the cursor could become null if
// a precondition is violated. Otherwise the cursor
// is null only if the tree is empty
return cursor != null;
}
public boolean hasLeft()
// post: returns true iff cursor has left child
{
return (cursor != null) && (cursor.left() != null);
}
public boolean isLeftChild()
// post: return true if cursor has parent & is left child
{
return (cursor != null) && cursor.isLeftChild();
}
public void moveLeft()
// pre: cursor is valid
// post: cursor moves to left child of pre-cursor,
// or off tree
{
prior = cursor;
wentLeft = true;
cursor = cursor.left();
}
public int height()
// post: returns height of cursor in tree
// or -1 if tree is empty
{
return BinaryTreeNode.height(cursor);
}
}
Reasonably efficient in terms of time and space complexity. 3 references per node, but makes operations easier. Can also get away w/ just left and right subtree references.
Please study the code in the text!
The code for the iterators in the text is quite complex, because it simulates recursion with a stack. We could have approached cycling through nodes in a different way that does not involve iterators if we know exactly what we want to do to the value stored in each node - call it doValueOp();
public void doInorder()
{
reset(); // move cursor to root
if (root != null) doRecInorder();
}
/*
post: Do inorder traversal of subtree pointed to by
cursor. Return cursor to starting point when done.
*/
public void doRecInorder()
{
if (hasLeft())
{
moveLeft();
doRecInorder();
moveUp();
}
value().doValueOp();
if (hasRight())
{
moveRight();
doRecInorder();
moveUp();
}
}
It's very important to move the cursor back to where you started, otherwise the recursive solution will not work.
Is there an array implementation of trees?