Gettysburg College

CS 216
Data Structures

Spring 2024

Assignment 6

Due: Mon, Mar 18, by 11:59pm
  • method stubs: BSTree.java
  • junit tests: BSTreeTest.java
  • coverage.pdf
  • api.pdf
Due: Thu, Mar 21, by 11:59pm
  • BSTree.java
  • BSTreeTest.java
  • CountRangeVisitor.java
  • coverage.pdf

Description

This is a continuation of Assignment 5. The goal is to add methods to the BSTree that make it possible to iterate through the tree and carry out the standard tree traversal algorithms.

JUnit Tests

Recall that each JUnit test has 3 or 4 lines. See section "JUnit Tests" in Assignment 5.

Required API

void preorder(Visitor<E> visitor)

Performs preorder traversal of this tree (see next method).

See section Visitor Interface below and copy the given code for Visitor and StringVisitor in their respective files in your own project.

Test this method in the JUnit tester by creating a StringVisitor and checking that the string value accumulated by the visitor matches the expected result (i.e. the items are listed in preorder sequence):

tree = load( ... );
visitor = create StringVisitor object;
tree.preorder( visitor );
assertEquals( visitor's value, "[...the items listed in preorder...]" );
assertEquals( tree.toString(), "[...the items listed in level order...]" );
The second line is needed before each traversal -- it ensures that the Visitor is reset/blank before the traversal. Declare the strVisitor at the top (as a data member like the tree).
void preorder(Visitor<E> visitor, Node curr)

Performs preorder traversal of the tree rooted at the given node curr.

Instead of printing the item of the current node, give it to the visit method of the Visitor.

void inorder(Visitor<E> visitor)

Performs inorder traversal of this tree (see next method). See method preorder for how to test.
void inorder(Visitor<E> visitor, Node curr)

Performs inorder traversal of the tree rooted at the given node curr.
void postorder(Visitor<E> visitor)

Performs postorder traversal of this tree (see next method). See method preorder for how to test.
void postorder(Visitor<E> visitor, Node curr)

Performs postorder traversal of the tree rooted at the given node curr.
boolean equals(Object other)

Returns true if the given object is equal to this tree (see next method; see also DLinkedList class)

Here is an example of the typical steps in implementing method equals(...); note that a typecast is required to convert from Object to the specific class:

Cat.java

JUnit: Compare the trees with themselves, with each other, and with things that are not trees. Note, that these tests are not likely to be sufficient.

boolean equals(Node root1, Node root2)

(recursive) Returns true if the trees rooted at the given nodes are identical, i.e. have identical items and identical structure.
Object clone()

Returns a copy of this tree (your class must implement the Cloneable interface).

The code for this method is given. It relies on the next method:

public Object clone()
{
    try {
        BSTree<E> copy = (BSTree<E>) super.clone();
        copy.comp = this.comp;
        copy.root = copyTree(this.root);
        return copy;
    }
    catch (CloneNotSupportedException e) {
        e.printStackTrace();
        return null;
    }
}
Node copyTree(Node curr)

(recursive) Returns a copy of the tree rooted at the given node (the two trees should be independent -- adding to or removing from one, should not affect the other).

BSTree(E[] items, Comparator<E> comp)

Creates the tree from the given preorder array of items (the array may be empty).

The code for this method is given. It relies on the next method:

BSTree(E[] items, Comparator<E> comp)
{
    this.comp = comp;
    this.root = rebuildPreorder(items, 0, items.length - 1);
}
To test in JUnit:
Integer[] numbers = { ... numbers listed in preorder sequence ... };

Create the tree with the new constructor and the numbers (not with load())

Assert that toString() produces the expected result.
Node rebuildPreorder(E[] items, int i, int j)

(recursive) Creates a tree from the given preorder array of items contained between indices [i,j] (the range is inclusive, i.e. contains all items that are in the corresponding tree).

Hint: Traverse the items to find index m that separates the range [i,j] into two sections [?,?] and [?,?] that contain items smaller than the root of the range [i,j]and items larger than the root (based on this tree's Comparator):

  • what is the order of the items in the two smaller ranges?
  • where is the value for the root of the sub-tree represented by the range [i,j]?

String toString()

Returns a string representation of this tree (level-by-level, left-to-right).

You do not have to write this method. It is used for testing, so continue using the version that was given in Assignment 5.

public String toString()
{
    return new BSTreeUtils<E>().toString( your-root );
}
String toStringLevel()

Returns a string representation of the tree listing the elements in level-order sequence in the format [a b c d].

Write your own version of toString() from Assignment 5 that uses the level-order iterator with a loop to collect all items in the tree and build the string (trimming at the end).

This method will test implicitly the level-order iterator in the JUnit tester.

All your test cases (from Assignments 5 and 6) should still work without modification, since the new toString() should give the same output as the helper from BSTreeUtils.

Iterator<E> iterator()

Returns an iterator over this tree that enumerates the elements in level-order sequence (method remove() is not supported).

The Level-Order Iterator works as follows: It keeps a Queue of nodes -- initially the root node is stored in the queue. Method next() removes a node from the Queue, inserts its children, and returns the value of the "popped" node.

What will be the order if the Queue is replaced with a Stack?

class CountRangeVisitor<E>

See the Visitor interface and the examples below and create a new class, CountRangeVisitor<E>, which counts the number of items in the tree that are in the given range [a,b] (inclusive):

CountRangeVisitor.java

The constructor of the CountRangeVisitor<E> takes the values of the desired range [a,b] and the comparator that was used to create the tree. These will be used to decide whether the visited item is inside the range. Note that the type of the range [a,b] is not Integer.

Test this in the JUnit tester in method test_CountRangeVisitorby creating CountRangeVisitor objects:

tree = load( ... );
visitor = create CountRangeVisitor object with some range and comparator;
tree.preorder( visitor );
assertEquals( visitor's value, ...the expected value...]" );
assertEquals( tree.toString(), "[...the items listed in level order...]" );

*** try different trees and different interesting ranges ***

There should be only one class called CountRangeVisitor -- no other subclasses or similar classes should be created. The effect of having different CountRangeVisitors is achieved by creating several objects of the class CountRangeVisitor by specifying the item type and supplying the correct comparator (the one that built the tree).

Visitor Interface

Here is the description of the Visitor interface (this is not part of the Java libraries).

Make sure to create two files in your project -- Visitor.java and StringVisitor.java -- with the Visitor interface and the StringVisitor class.

The PrintVisitor is not needed and is only given as an extra example.

The main Visitor interface -- other visitors must implement method visit(E item).

Save this in your project.

public interface Visitor<E>
{
    public void visit(E item);
}

Visitor class that can be used to collect the values in the tree during the traversal.

Save this in your project. This visitor has its own data member to accumulate the result and a method getValue to report the final result:

public class StringVisitor<E> implements Visitor<E>
{
    private String str;            // data member to collect the visited items

    public StringVisitor()
    {
        str = "";                  // start with a blank string
    }

    @Override
    public void visit(E item)      // required method from the Visitor interface
    {
        str += item + " ";         // append current item to overall string
    }

    public String getValue()       // use after the traversal to get the overall string
    {
        return "[" + str.trim() + "]";
    }
}

Visitor class that can be used to display the values in the tree on one line during the traversal

public class PrintVisitor<E> implements Visitor<E>
{
    @Override
    public void visit(E item)
    {
        System.out.print(item + " ");
    }
}