Copyright © Cay S. Horstmann 2012
This work is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 United
States License.
java Trees1 10
and java Trees1 20
.)seq—
see Lab 9.)Complete this program. (You need the
BinaryTree
class from Chapter 17 Section 2.)
What is the code of your randomTree method?
toString
method, and we will do that
presently. But first, let's look at the tree in the debugger. Set a
breakpoint at the last line of main
and run the program. Get
out a sheet of paper. Inspect t
. Keep expanding the
left
and right
nodes. From the display, sketch
the tree, like you did in Lab 7.
Hand the sketch to the lab instructor.
toString
method. Ideally, we might like
something like
6 / \ 3 2 / \ \ 1 1 1
but that's pretty hard to do in general. Instead, let's have a one-dimensional representation, something of the form
left subtree data right subtree
Let's do that. Add a method
public String toString()
to the BinaryTree
class. It must call a recursive method on
the root node. Let's call it toString
as well:
public static String toString(Node n)
That's where the actual work gets done. (Look at height
as
another example of how this is done.)
In the Node.toString
method, return a string as described
above, with the left, data, and right separated by spaces. If the node is
null
, return "."
.
What is your code?
(left subtree data right subtree)
Do this. What is your output now?
((. 2 (. 1 .)) 6 ((. 1 .) 3 (. 1 .)))
Draw the correspnding tree.
for (BinaryTree t1 : l1) { for (BinaryTree t2 : l2) { BinaryTree t = new BinaryTree(n, t1, t2); ... } }
Put each tree in a list l and return it.
Complete the allTrees
method in this class. What is your code?
wc
, as
shown in Lab 9.)Problem #1. The BinarySearchTree class in Chapter 17 Section 3 is different from the BinaryTree class that we used so far.
Why can't we just change BinaryTree
to
BinarySearchTree
in Trees3
?
BinarySearchTree
have a constructor
public BinarySearchTree(Object rootData, BinarySearchTree left, BinarySearchTree right)
private
from
private Node root;
in BinarySearchTree
. Now root
(and the
Node
class) are package-visible. When we add a class
Trees4
to the default package, it can access the
root
field.
Next, declare the Node
class as static
:
static class Node
This is a very technical detail—it means that a Node
can
be constructed outside a BinarySearchTree
.
For example, suppose you have trees t1
and t2
,
and now you want to make a new tree with both of them, you can use this
brute-force code:
BinarySearchTree t = new BinarySearchTree(); t.root = new BinarySearchTree.Node(); t.root.left = t1.root; t.root.right = t2.root;
Not very object-oriented, but it's ok for a test harness that is meant to test the internals of the implementation.
Using this technique, make a copy of Trees3, call it Trees4, change all
BinaryTree to BinarySearchTree, and construct the trees as described above.
Also set t.root.data = n
in the second and third branch;
e.g.
for (BinarySearchTree t1 : l1) { for (BinarySearchTree t2 : l2) { BinarySearchTree t = new BinarySearchTree(); t.root = new BinarySearchTree.Node(); t.root.data = n; ... } }
What does your program print? Why?
toString
to the BinarySearchTree
class.
In toString
, you can't just use print
. Why?toString
in the same way as you did before. Now
what does your program print? Here is our problem. We have a tree like
* / \ * * / \ \ * * *
and now we want to fill in the data
4 / \ 2 5 / \ \ 1 3 6
Here is a recursive way of doing it. Look at the root. Fill in the left subtree, starting with 1, and have the recursive call tell you the next number to be used (4 in the example). Then fill in the root with the that data. Then fill in the right subtree starting with the next value (here 5). And ask it what its next number is (7). Return that number to the caller.
In Trees4
, Implement a method
public static int fill(BinarySearchTree.Node n, int start)
that uses this strategy. What is your code?
fill(t.root, 1);
in main
. What is your printout?
main
method to
public static void main(String[] args) { List<BinarySearchTree> trees = allTrees(6); int k = 1; for (BinarySearchTree t : trees) { fill(t.root, k); k++; } for (BinarySearchTree t : trees) { System.out.println(t); } }
What values to you expect to have in the first tree? The second tree? The kth tree?
Then the second mutation overwrites the first.
Let's see if we have shared nodes in our trees.
When you make a new Node
, you get a fresh, new one. Where
does that happen in allTrees
?
a.left = n;
then both a.left
and n
point to the same node.
Where does that happen in allTrees
?
remove
method, which we want to test, mutates the tree on
which it operates.
So, we need to make a copy of each tree.
Copying a tree seems generally useful, so let's add a method
public BinarySearchTree copy()
to the BinarySearchTree
class.
How do you copy a tree? You construct a new root node and populate it with copies of the left and right subtrees. That's best done in a recursive method
public Node copy(Node n) { if (n == null) return null; else { Node newRoot = ...; ... ... ... return newRoot; } }
Add the copy
methods to the BinarySearchTree
class. What are your complete methods?
System.out.println(t);
in Trees4
to
System.out.println(t.copy());
If you did everything right, you should get the same output.
remove
method. Here's the plan. For a given n
,
Add a JUnit class RemoveTest
to your NetBeans project. What
did you do to do that?
RemoveTest
class
public void testRemove(int n)
Do just what the pseudocode did. Call Trees4.allTrees
,
Trees4.fill
, find
(in the
BinarySearchTree
), assertTrue
, and
assertFalse
(from JUnit).
What is your method?
@Test
. What happens if you add @Test
to
public void testRemove(int n)
?@Test
and add
@Test public void testRemove6() { testRemove(6); }
What happens?
remove
method
worked. Just to see what happens when there is an error, locate the
if
statement
if (toBeRemoved.left == null) { newChild = toBeRemoved.right; }
in BinarySearchTree.remove
and inside the body, change
right
to left
. Run the test again. What
happens?
remove
method. When you look at section
17.3.3 of your textbook, you will notice that the case “Removing a node
with two children” makes an arbitrary choice. The node to be removed is
replaced with the smallest value in the right subtree, when it could
equally well be replaced with the largest value in the left subtree.
Consider this BST:
2 / \ 1 7 / \ 4 11 / \ / \ 3 5 10 12 \ / 6 8 \ 9
We want to remove 7.
How does the removal unfold, as described in the book?
remove
method to do that. List all the
changes that you had made.