290 likes | 399 Views
Binary Trees. Like a list, a (binary) tree can be empty or non-empty. In class we will explore a state-based implementation, similar to the LRStruct You are expected to read about alternate implementation strategies in the textbook. Methods of a Binary Recursive Structure (BRStruct).
E N D
Binary Trees • Like a list, a (binary) tree can be empty or non-empty. • In class we will explore a state-based implementation, similar to the LRStruct • You are expected to read about alternate implementation strategies in the textbook
Methods of aBinary Recursive Structure (BRStruct) • method to grow tree: insertRoot • method to shrink tree: removeRoot • accessor/mutator pair for datum: setDatum/getDatum • accessor/mutator pair for left: setLeft/getLeft • accessor/mutator pair for right: setRight/getRight • visitor support: execute
State transitions • Empty NonEmpty: insertRoot on an empty tree • NonEmpty Empty: removeRoot from a tree that is a leaf • A leaf is a tree both of whose children are empty. • No other state transitions can occur.
Moreover… • insertRoot throws an exception if called on a nonEmpty tree • removeRoot throws an exception if called on a non-leaf tree • Isn’t this very restrictive?
Yes…but • It is very restrictive, but for good reasons: • What would it mean to add a root to a tree that already has one? • If you could remove the root from a tree with nonEmpty children, what would become of those children?
Usage of a BRS • A BRStruct is not used “raw”, but is used to implement more specialized trees. • Consider how we defined the SortedList in terms of the LRStruct: by composition. • We will now explore how to define a sorted binary tree (a Binary Search Tree) by composition with a BRStruct.
Binary Search Tree (BST) • A binary search tree (BST) is a binary tree which maintains its elements in order. • The BST order condition requires that, for every non-empty tree, the value at the root is greater than every value in the tree’s left subtree, and less than every value in the tree’s right subtree.
Example 1Does this tree satisfy the BST order condition? Fred Betty Wilma Barney Pebbles
No!Barney < Betty < Pebbles > Fred < Wilma Fred Betty Wilma Barney Pebbles
Example 2Does this tree satisfy the BST order condition? Fred Betty Wilma Barney Pebbles
Yes!Barney < Betty < Fred < Pebbles < Wilma Fred Betty Wilma Barney Pebbles
Example 3Does this tree satisfy the BST order condition? Fred Betty Wilma Barney Pebbles
No!Betty > Barney < Fred < Pebbles < Wilma Fred Betty Wilma Barney Pebbles
Example 4…but if we swap Betty & Barney, we restore order! Fred Barney Wilma Betty Pebbles
Operations on aBST<E extends Comparable> • public BST insert(E item) • public BST remove(E item) • public boolean member(E item) • How do we support these? They don’t exist as methods on the BRStruct.
Visitors to the rescue! • Visitors on the BRStruct have the same basic structure as on the LRStruct: they deal with two cases: emptyCase and nonEmpty Case. • In our example: public BRStruct<E> emptyCase(BRStruct<E> host, E arg) public BRStruct<E> nonEmptyCase(BRStruct<E> host, E arg)
Example: toStringVisitor public class ToStringVisitor extends IAlgo { public static final ToStringVisitor SINGLETON = new ToStringVisitor(); private ToStringVisitor() {} public Object emptyCase(BRS host, Object input) { return "[]"; } public Object nonEmptyCase(BRS host, Object input) { return "[" + host.getLeft().execute(this, null) + " " + host.getRoot().toString() + " " + host.getRight().execute(this, null) + "]"; } }
Back to the BST • Let’s first consider the insert operation. • We must consider two possibilities: • the underlying BRStruct is empty • just insertRoot • the underlying BRStruct is nonEmpty • we can’t insertRoot, because the BRStruct is nonEmpty • compare new item with root – determine whether new item belongs in left or right subtree – insert recursively into correct subtree • EXERCISE: DEFINE THIS VISITOR
A first cut at the visitor public class InsertVisitor<E extends Comparable> implements IAlgo<E,E,Object> { public Object emptyCase(BRStruct<E> host, E item) { return host.insertRoot(item); } public Object nonEmptyCase(BRStruct<E> host, E item) { if (item.compareTo(host.getDatum()) < 0 ) { // item belongs in left subtree return host.getLeft().execute(this, item); } else if (item.compareTo(host.getRoot()) > 0 ) { // item belongs in right subtree return host.getRight().execute(this, item); } else { // item is already in tree (Note UNIQUENESS ASSUMPTION) return host; } } }
How about determining membership for an item? • We must consider two possibilities: • the underlying BRStruct is empty • just item was not found • the underlying BRStruct is nonEmpty • compare new item with root – determine whether new item has been found, or whether it would be in left or right subtree – look recursively into correct subtree • EXERCISE: DEFINE THIS VISITOR
A first cut at the visitor public class MemberVisitor<E extends Comparable> implements IAlgo<E,E,Boolean> { public Boolean emptyCase(BRStruct<E> host, E item) { return false; } public Boolean nonEmptyCase(BRStruct<E> host, E item) { if (item.compareTo(host.getDatum()) < 0 ) { // item belongs in left subtree return host.getLeft().execute(this, item); } else if (item.compareTo(host.getRoot()) > 0 ) { // item belongs in right subtree return host.getRight().execute(this, item); } else { // item is in tree return true; } } }
Did you notice similarity? • The structure of the insert and membership visitors was the same. • A small number of details differed. • Let’s unify! • EXERCISE: DEFINE THIS VISITOR
The find visitor public class MemberVisitor<E extends Comparable> implements IAlgo<E,E,BRStruct<E>> { public BRStruct<E> emptyCase(BRStruct<E> host, E item) { // item is in not in tree, but would belong in host return host; } public BRStruct<E> nonEmptyCase(BRStruct<E> host, E item) { if (item.compareTo(host.getDatum()) < 0 ) { // item belongs in left subtree return host.getLeft().execute(this, item); } else if (item.compareTo(host.getRoot()) > 0 ) { // item belongs in right subtree return host.getRight().execute(this, item); } else { // item appears in tree as datum of host return host; } } }
Look at BST implementation • insert, remove and member ALL make use of the same FindVisitor: • first find the insertionPoint, • then do the right thing.
The insert method public BST insert(E item) { BRStruct<E> tree = _tree.execute(new FindVisitor<E>(), item); only insertiteminto insertPoint if empty (duplicates ignored) return this; } EXERCISE: SOLVE PURPLE PROBLEM
The insert method public BST insert(E item) { BRStruct<E> tree = _tree.execute(new FindVisitor<E>(), item); tree.execute(new IAlgo<E,E,Object>() { public Object emptyCase(BRStruct<E> host, E input) { host.insertRoot(input); return null; } public Object nonEmptyCase(BRStruct<E> host, E input) { return null; } }, item); return this; }
The member method public BST member(E item) { BRStruct<E> tree = _tree.execute(new FindVisitor<E>(), item); return whether insertPoint is empty or nonEmpty } EXERCISE: SOLVE PURPLE PROBLEM
The member method public BST member(E item) { BRStruct<E> tree = _tree.execute(new FindVisitor<E>(), item); return tree.execute(new IAlgo<Object,E,Boolean>() { public Boolean emptyCase(BRS host, Object _) { return false; } public Boolean nonEmptyCase(BRS host, Object _) { return true; } }, null); }
The remove method? • Next time!