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. { 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.
Is there an array implementation of trees?
Can load in exactly same way: read root, read and attach left subtree, read and attach right subtree.
File operations are pretty straightforward:
// create a new input file with name fileName. DataInputStream inFile = new DataInputStream(new FileInputStream(fileName)); // read character from inFile and store in letter char letter = inFile.readChar(); // read string from inFile and store in contents String contents = inFile.readUTF(); inFile.close(); // close file // create a new output file with name fileName. DataOutputStream outFile = new DataOutputStream(new FileOutputStream(fileName)); // write character from letter onto outFile outFile.writeChar(letter); // write string from contents onto outFile outFile.writeUTF(contents); outfile.close(); // close file
These classes are found in package java.io, so you must include an import statement for them.
File operations can throw exceptions because not find file, hit end of file when reading, etc.! In Java, one must test for exceptions and provide code to execute when exceptions are raised.
Java file operations may raise exceptions of type IOException. Must surround code that may throw this exception by:
try {//code using files} catch (IOException exc) {//code to execute if exception raised}
In the above, exc is a parameter of type IOException. You can send it a toString message to get info on what went wrong in the block that follows it.
You can either put all of the code into one single try-catch block or put each command that may raise the exception in such a block. You will get syntax errors if you do not surround these file messages with a try-catch block.
Your program should not just ignore these exceptions. At a minimum it should print out a message and return to a consistent state.
What do we know about first possible characters of Factor, Term, or Expression?
Can use to help catch errors.
Look at how 3 * 7 + 6 / 2 - (3 + 7) would be understood as a tree.
3*7 is a term because both 3 and 7 are factors, similarly 6/2 is a term.
(3+7) is a factor because 3 + 7 is is an expression.
Because it is a factor, it is also a term.
Therefore 3 * 7 + 6 / 2 - (3 + 7) is of the form term1 + term2 - term3, and hence is an expression!
Exercise: Write out the corresponding tree (remember operations of the same precedence are done from left to right.)
Once you have the tree, how do you evaluate it?
See Parser code on-line.
Two things to notice: