Due before 10pm, Sunday, April 07
In this lab you will implement a recursive binary tree structure and associated methods. The purpose of this lab is to:
I've provided a GUI-based Java application which you can use to test your methods. You should get the starter code from BinaryTrees.zip. As with previous labs, once you unjar this file, you should start a new project in eclipse from the directory storing the unzipped files. The java files you should see are:
As noted above, all files are complete, except for TreeLoader.java, which contains only a stub of the loadTreeFromFile method, and TreeMethods.java, which contains only stub routines for several methods with operate on binary trees. Don't worry about what these files do, for now, we will explain them soon enogh.
The first part of your lab is to get the GUI up and running. The GUI is going to help you test the methods you write, so it's best to have it working right from the start. In order to do so, you are going to have to get a bare-bones BinaryTree, EmptyTree, and ConsTree.
As you may recall from class, a binary tree is either:
Therefore, you should create three classes:
At this point, all of the compile errors should be gone from all files. This means you should be able to run two of the given programs to make sure they are working. They are pretty boring right now, but let's just give them a shot.
The first program is TreeLoader, which has as its main method a simple testing program. It accepts a filename as a command-line argument, loads a binary tree from the file, and then displays it in the same format that is used for the TreeMethodApplication. If you look at the TreeLoader class, you can see that the loadTreeFromFile method, by default, just returns an EmptyTree. This is because you will be filling in this method momentarily. The effect of this default behaviour is that the program just prints out an Empty tree, every time, irrespective of the file you give it. And since we haven't overridden the toString methods in our tree classes to do anything interesting, a call to this main method should just print out the address of the empty tree that we construct. E.g.
% java TreeLoader tree1.txt
EmptyTree@2c6f7ce9
Try this out in the console and check that it works. You won't get the same address we do for your EmptyTree, but it should be something similar.
The second program is TreeMethodApplication. Launch it now.
As you should see, this application's GUI frame has two text areas for output, and a group of buttons for user commands. The "load file" button allows the user to load a binary tree from a disk file. The jar file also contains some sample files you can use for testing, in the format described below.
When a file is loaded, a binary tree object is created, and the tree is displayed in the left area of the frame, using the loadTreeFromFile method. As mentioned above, right now this is just constructing an empty tree, but you will later implement it so that the tree from the file is displayed in inorder sequence, with the data from each node on a separate line. Each node is indented one space from its parent, so the structure of the tree is visible. (Basically, turn your head 90 degrees counterclockwise and you should see the tree as you might draw it on paper.)
The area on the right side of the frame is used for the output of commands. The output may be a single number, as in the "height" method, or it may be another tree, as in the "mirror" method. You can press these buttons and they will display some (incorrect) default behaviour we put in the TreeMethods class.
Your task for this part is to implement the loadTreeFromFile method in the TreeLoader class. We'll walk you through it.
The test files for the GUI list the nodes of the tree in postorder sequence, one node per line. Each line contains a string, which is the data value stored in the node, and two tag bits. The first tag bit indicates whether or not this node has a left child or not. (1=oui,0=non). The second tag bit indicates whether the node has a right child or not.
For example, the tree
34 / \ 74 83 \ / \ 32 63 18 / 29
is represented as
32 0 0 74 0 1 29 0 0 63 1 0 18 0 0 83 1 1 34 1 1
The information in the file is sufficient to uniquely determine a binary tree. The tree can be constructed from the file using the following algorithm:
create an empty stack of binary trees (i.e. Stack<BinaryTree<String>>
)
while there is another line of input in the file {
read a line (containing data and two tags)
if right tag is 1, pop an element from the stack and call it right
if left tag is 1, pop an element from the stack and call it left
create a new binary tree with data as its root and
left and right as its subtrees (if they exist)
push the new tree onto your stack
}
when you exit the while loop, the stack should contain one element
this element is the the tree represented by the file data (so return it!)
If the stack is empty, the tree has no nodes, so the tree must be an EmptyTree.
NOTE: the input file is left then right, but the algorithm
uses the right value first, then the left. Not a typo.
You need to implement this algorithm in the loadTreeFromFile method, with the following method signature:
public BinaryTree<String> loadTreeFromFile(String filename)
The method should create a Scanner object to read strings from the file whose name is given as an argument. It should create a Stack of BinaryTree<String> objects to use as a work area. You can use the Scanner and Stack classes from java.util (unless you have über-confidence in your own MyStack class.)
NOTE: Use Java generics in your code when declaring and instantiating a tree or stack. (For example, use BinaryTree<String> and Stack<BinaryTree<String>>.) Your code should not generate any warning messages when compiled.
Test out your program, using either or both of TreeLoader and TreeMethodApplication. Hopefully you don't get any runtime exceptions! But you may notice that your tree is displaying itself as a pointer ...
Therefore you need to write toString methods in both the EmptyTree and ConsTree classes. (There is no need to add a no-argument toString() declaration in the abstract BinaryTree class, because all Objects have toString by default. You do have to add the one-parameter version described below, because recursive calls to the left and right children are applied to BinaryTrees, not just to ConsTrees.) I'm going to suggest a way to do this that produces the tree, rotated to the left by 90 degrees. That is, you need to turn your head so that the left side of the screen is the "top" of the tree. The example tree from above would look like the following:
18 / 83 \ 63 \ 29 / 34 \ 32 / 74
It's not art, people, but it works. So, how can you do this yourself? You may want to think about it a bit, because it's a cool puzzle. You need to use a helper toString method that takes an indentation as a parameter. Each recursive call to the subtrees increases the indentation by some number of spaces. Anyway, here is the methods that you should add to your ConsTree class:
public String toString( String indent ) { return right.toString( indent + " " ) + "\n" + indent + "/\n" + indent + data + "\n" + indent + "\\" + left.toString( indent + " "); } public String toString() { return toString(""); }
For the EmptyTree class, things are simpler, as usual:
public String toString( String indent ) { return ""; } public String toString() { return toString(""); }
Feel free to mess around with these a bit if you can make improvements! And add the method header of this method to your BinaryTree class, because this isn't your usual toString method (it has a different parameter list.)
Now, for each of the methods listed below, do the following:
Note that you should not need any loops in any of these methods.
For example, for the height method below, you will:
public abstract int height( );
public int height( ) { return -1; }
public int height( ) { return 1 + Math.max(left.height(),right.height()); }
Here are the list of methods:
83 (2) \ 63 (1) \ 29 (0) / 34 (2) \ 32 (0) / 74 (1)
The remaining methods assume that the strings stored in the binary tree are actually integers. Your code will have to convert them to ints using the Integer.parseInt() method. (If you apply one of these methods to a tree containing non-integer data, an exception will be thrown and a message displayed in the right panel of the application window. Test files 6-10 contain integer data.)
Finally, you'll create some methods that will traverse the nodes in the tree.
E D C A B
A B C D E
E A C B D
The provided source code contains 10 test files from which you can read binary trees.
Look through your programs and make sure you've included your name at the top of all of them.
You need to create a file called "README" that contains the following information:
If you adhered to the honor code in this assignment, add the following statement to your README file:
I have adhered to the Honor Code in this assignment.
You now just need to electronically handin all your files. Assignment is 6.
Don't forget to run lshand and verify that things were submitted.