Gettysburg College

CS 216
Data Structures and Algorithms

Fall 2024

Assignment 6

Due: Mon, Oct 7, by 11:59pm
  • method stubs: BSTree.java
  • junit tests: BSTreeTest.java
  • coverage.pdf
  • api.pdf
Due: Thu, Oct 10, by 11:59pm
  • BSTree.java
  • BSTreeTest.java
  • CountRangeConsumer.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.

Required API

void preorder(Consumer<E> consumer)

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

See section Consumer Interface below and copy the given code for StringConsumer in its own file in the project.

Initially, could ignore the consumer and write the code as if the intent is to print the items during the traversal. Then, instead of giving the items to the "printer", give them to the consumer.

JUnit: This will require 5 lines. Create StringConsumer and check that the string value accumulated by the consumer matches the expected result (i.e. the items are listed in preorder sequence):

tree = load( ... );
consumer = create StringConsumer object;
tree.preorder( consumer );
assertEquals( consumer's value, "[...the items listed in preorder...]" );
assertEquals( tree.toString(), "[...the items listed in level order...]" );
The second line is needed before each traversal to ensure that the consumer is reset/blank before the traversal. You could declare the consumer object at the top (as a data member like the tree).
void preorder(Consumer<E> consumer, Node curr)

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

Nothing is printed here. Instead the items are given to the consumer.

void inorder(Consumer<E> consumer)

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

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

Performs postorder traversal of this tree (see next method). See method preorder for how to test.
void postorder(Consumer<E> consumer, 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(Comparator<E> comp, E... items)

Creates the tree from the given array of items (see below for the E... notation). It is assumed that the items in the array are arranged in preorder sequence.

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

BSTree(Comparator<E> comp, E... items)
{
    this.comp = comp;
    this.root = rebuildPreorder(items, 0, items.length - 1);
}

The notation E... is roughly equivalent to declaring a primitive array from CS111, but it allows for a nicer way of calling the constructor (see method load in BSTreeTest).

JUnit: Instead of method load use the new constructor to build the tree:

tree = new BSTree<¿>( theComp, u, v, w x, y );
Assert that toString() produces the expected result. (only 2 lines)
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 [?,?].

String toString()

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

The goal is to replace the version based on BSTreeUtils with your own version that uses the level-order iterator to collect all items in the tree and build the string. The two versions should produce identical output.

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

All 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 keeps a Queue of waiting nodes and gives values one at a time, possibly adding on to the waiting nodes.

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

@Test
void test_iterFails()

Add this method in the Tester class. For each type of tree (empty, single, multi) it tests the iterator of that tree for all possible failing conditions of the method next(), i.e. the conditions under which these methods throw exceptions.

The goal is to have each line in the Iterator class marked green.

Here is the structure of the tests: one group per list, for a total of 3 groups, each group is longer than 4 lines:

tree = load( m,y,n,u,m,b,e,r,s);
Iterator<???> iter = tree.iterator();
    // rule-of-3 does not apply, ok (necessary) to have multiples of this line here:
    iter.next(), ... [sometimes in assertThrows]
assert toSting for the tree
CountRangeConsumer

See the Consumer interface and the examples below and create a new generic class, CountRangeConsumer, which counts the number of items in the tree that are in the given range [a,b] (inclusive):

Create class CountRangeConsumer in its own file in the project. Here is a starting point:

CountRangeConsumer.java

The constructor of the CountRangeConsumer 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 accepted item is inside the range. Note that the type of the range [a,b] is not Integer.

JUnit: Test in method test_CountRangeConsumer by creating CountRangeConsumer objects:

tree = load( ... );
consumer = create CountRangeConsumer object with some range and comparator;
tree.preorder( consumer );
assertEquals( consumer'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 CountRangeConsumer -- no other subclasses or similar classes should be created. The effect of having different CountRangeConsumers is achieved by creating several objects of the class CountRangeConsumer by specifying the item type and supplying the correct comparator (the one that built the tree).

Consumer Interface

Here is the description of the Consumer interface. Only method accept is relevant for this assignment:

Consumer Interface

Save StringConsumer in its own file in the project. This will be used for testing some of the BSTree methods.

The PrintConsumer is not needed and is only given as an example.

StringConsumer

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

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

public class StringConsumer<T> implements Consumer<T>
{
    private String str;            // data member to collect the visited items

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

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

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

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

public class PrintConsumer<T> implements Consumer<T>
{
    @Override
    public void accept(T item)
    {
        System.out.print(item + " ");
    }
}

What to turn in

Upload the .java and .pdf files in the Moodle dropbox. (Do NOT upload .html files.)