I am trying to write the following method for a lab assignment but have become very stuck on it. We are working with binary search tree's and they have asked for this method "int sizeBelow(T high) returns the number of elements in the tree that are strictly less than high" If someone could help me figure out how to write this it would be really appreciated! Been stuck on this for way too long
package week11;
import java.util.Scanner;
import static week11.LinkedBST.Direction.*;
/**
* A binary tree implementation using links. We assume that the tree
* is not to store 'null' elements. In particular if the root node
* *is* null then the tree is empty. This can only occur if a tree
* is initially constructed with no arguments, or if we remove the
* only element from a tree.
*
* #author Michael Albert, Iain Hewson
*/
public class LinkedBST<T extends Comparable<T>> {
/** The element held at the root of this tree. */
private T root;
/** The left subtree of this tree. */
private LinkedBST<T> left;
/** The right subtree of this tree. */
private LinkedBST<T> right;
/**
* Creates a BST with the given value.
*
* #param value to store at the root of this LinkedBST.
*/
public LinkedBST(T value) {
root = value;
left = null;
right = null;
}
/**
* Creates a new empty BST.
*/
public LinkedBST() {
this(null);
}
/**
* Adds an element to this BST if it isn't already there.
*
* #param element an element to be added.
*/
public void add(T element) {
if (root == null) {
root = element;
}
int comparison = root.compareTo(element);
if (comparison > 0) {
if (left == null) {
left = new LinkedBST<T>(element);
} else {
left.add(element);
}
} else if (comparison < 0) {
if (right == null) {
right = new LinkedBST<T>(element);
} else {
right.add(element);
}
}
}
/**
* Returns the height of this tree.
*
* #return the height of this tree.
*/
public int height() {
int leftH = 0, rightH = 0;
if (root == null) {
return 0;
}
if (right != null) {
rightH = 1 + right.height();
}
if (left != null) {
leftH = 1 + left.height();
}
return Math.max(leftH, rightH);
}
/**
* Searches for the given target within this tree.
*
* #param target
* #return true if target is found, otherwise false.
*/
public boolean search(T target) {
boolean lefth = false, righth = false;
if (root == null) {
return false;
}
int comparison = root.compareTo(target);
if (comparison == 0) {
return true;
}
if (comparison > 0) {
if (left != null) {
lefth = left.search(target);
}
return lefth;
}
if (comparison < 0) {
if (right != null) {
righth = right.search(target);
}
return righth;
}
return false;
}
/**
* Returns the size of this BST.
*
* #return the size of this BST.
*/
public int size() {
int lefth = 0, righth = 0;
if (root == null) {
return 0;
}
if (right != null) {
righth = right.size();
}
if (left != null) {
lefth = left.size();
}
return 1 + lefth + righth;
}
/**
* Returns how many elements are greater than or equal to the
* parameter <code>low</code>.
*
* #param low the lower bound to use when counting elements.
* #return how many elements are greater than or equal to the
* parameter <code>low</code>.
*/
public int sizeAbove(T low) {
if (root == null) {
return 0;
}
return 0;
}
/**
* Returns how many elements are less than the parameter
* <code>high</code>.
*
* #param high the element to compare when counting elements.
* #return how many elements are less than the parameter
* <code>high</code>.
*/
public int sizeBelow(T high) {
// implement this for part 2
return 0;
}
/**
* Returns how many elements are greater than or equal to the
* parameter <code>low</code> and less than the parameter
* <code>high</code>.
*
* #param low the lower bound to use when counting elements.
* #param high the upper bound to use when counting elements.
* #return how many elements are between low (inclusive) and
* high (exclusive).
*/
public int sizeBetween(T low, T high) {
// implement this for part 2
return 0;
}
/**
* Removes the given element from this tree if it is present.
*
* #param element the element to remove.
*/
public void remove(T element) {
// implement this method from the lectures if you
// want to do the extension exercises
}
/** The direction used when creating a representation of this tree. */
enum Direction {LEFT, RIGHT, NO};
/**
* Recursively generates a representation of this tree.
*
* #param curr the current line being generated.
* #param dir the direction of the last link followed.
* #param result the representation generated so far.
* #return a representation of this tree.
*/
public StringBuilder str(String curr, Direction dir, StringBuilder result) {
if(right != null) {
right.str(curr + (dir == LEFT ? "│ " : " "), RIGHT, result);
}
if (root != null) {
result.append(curr + (dir == RIGHT ? "┌─ " :
dir == LEFT ? "└─ " : " ") + root + "\n");
}
if(left != null) {
left.str(curr + (dir == RIGHT ? "│ " : " "), LEFT, result);
}
return result;
}
#Override
public String toString() {
return str("", NO, new StringBuilder()).toString();
}
/**
* Entry point of program (used for testing).
* Valid commands are:
* <pre>
* a (add) item(s) - calls add with each item
* f (find) item - calls search with item
* p (print) - calls toString
* h (height) - calls height
* s (size) - calls size
* sa (sizeabove) low - calls sizeAbove(low)
* sb (sizebelow) high - calls sizeBelow(high)
* si (sizeinbetween) low high - calls sizeBetween(low,high)
* </pre>
* Return values of methods are printed to stdout.
*
* #param args command line arguments are not used.
*/
public static void main(String[] args) {
LinkedBST<String> tree = new LinkedBST<>();
Scanner input = new Scanner(System.in);
while (input.hasNextLine()) {
Scanner line = new Scanner(input.nextLine());
if (line.hasNext()) {
String command = line.next();
switch (command) {
case "a": case "add":
while (line.hasNext()) {
tree.add(line.next());
}
break;
case "f": case "find":
if (line.hasNext()) {
System.out.println(tree.search(line.next()));
}
break;
case "p": case "print":
System.out.print(tree);
break;
case "h": case "height":
System.out.println(tree.height());
break;
case "s": case "size":
System.out.println(tree.size());
break;
case "sa": case "sizeabove":
if (line.hasNext()) {
String low = line.next();
System.out.println(tree.sizeAbove(low));
}
break;
case "sb": case "sizebelow":
if (line.hasNext()) {
package week11;
import java.util.Scanner;
import static week11.LinkedBST.Direction.*;
/**
* A binary tree implementation using links. We assume that the tree
* is not to store 'null' elements. In particular if the root node
* *is* null then the tree is empty. This can only occur if a tree
* is initially constructed with no arguments, or if we remove the
* only element from a tree.
*
* #author Michael Albert, Iain Hewson
*/
public class LinkedBST<T extends Comparable<T>> {
/** The element held at the root of this tree. */
private T root;
/** The left subtree of this tree. */
private LinkedBST<T> left;
/** The right subtree of this tree. */
private LinkedBST<T> right;
/**
* Creates a BST with the given value.
*
* #param value to store at the root of this LinkedBST.
*/
public LinkedBST(T value) {
root = value;
left = null;
right = null;
}
/**
* Creates a new empty BST.
*/
public LinkedBST() {
this(null);
}
/**
* Adds an element to this BST if it isn't already there.
*
* #param element an element to be added.
*/
public void add(T element) {
if (root == null) {
root = element;
}
int comparison = root.compareTo(element);
if (comparison > 0) {
if (left == null) {
left = new LinkedBST<T>(element);
} else {
left.add(element);
}
} else if (comparison < 0) {
if (right == null) {
right = new LinkedBST<T>(element);
} else {
right.add(element);
}
}
}
/**
* Returns the height of this tree.
*
* #return the height of this tree.
*/
public int height() {
int leftH = 0, rightH = 0;
if (root == null) {
return 0;
}
if (right != null) {
rightH = 1 + right.height();
}
if (left != null) {
leftH = 1 + left.height();
}
return Math.max(leftH, rightH);
}
/**
* Searches for the given target within this tree.
*
* #param target
* #return true if target is found, otherwise false.
*/
public boolean search(T target) {
boolean lefth = false, righth = false;
if (root == null) {
return false;
}
int comparison = root.compareTo(target);
if (comparison == 0) {
return true;
}
if (comparison > 0) {
if (left != null) {
lefth = left.search(target);
}
return lefth;
}
if (comparison < 0) {
if (right != null) {
righth = right.search(target);
}
return righth;
}
return false;
}
/**
* Returns the size of this BST.
*
* #return the size of this BST.
*/
public int size() {
int lefth = 0, righth = 0;
if (root == null) {
return 0;
}
if (right != null) {
righth = right.size();
}
if (left != null) {
lefth = left.size();
}
return 1 + lefth + righth;
}
/**
* Returns how many elements are greater than or equal to the
* parameter <code>low</code>.
*
* #param low the lower bound to use when counting elements.
* #return how many elements are greater than or equal to the
* parameter <code>low</code>.
*/
public int sizeAbove(T low) {
if (root == null) {
return 0;
}
return 0;
}
/**
* Returns how many elements are less than the parameter
* <code>high</code>.
*
* #param high the element to compare when counting elements.
* #return how many elements are less than the parameter
* <code>high</code>.
*/
public int sizeBelow(T high) {
// implement this for part 2
return 0;
}
/**
* Returns how many elements are greater than or equal to the
* parameter <code>low</code> and less than the parameter
* <code>high</code>.
*
* #param low the lower bound to use when counting elements.
* #param high the upper bound to use when counting elements.
* #return how many elements are between low (inclusive) and
* high (exclusive).
*/
public int sizeBetween(T low, T high) {
// implement this for part 2
return 0;
}
/**
* Removes the given element from this tree if it is present.
*
* #param element the element to remove.
*/
public void remove(T element) {
// implement this method from the lectures if you
// want to do the extension exercises
}
/** The direction used when creating a representation of this tree. */
enum Direction {LEFT, RIGHT, NO};
/**
* Recursively generates a representation of this tree.
*
* #param curr the current line being generated.
* #param dir the direction of the last link followed.
* #param result the representation generated so far.
* #return a representation of this tree.
*/
public StringBuilder str(String curr, Direction dir, StringBuilder result) {
if(right != null) {
right.str(curr + (dir == LEFT ? "│ " : " "), RIGHT, result);
}
if (root != null) {
result.append(curr + (dir == RIGHT ? "┌─ " :
dir == LEFT ? "└─ " : " ") + root + "\n");
}
if(left != null) {
left.str(curr + (dir == RIGHT ? "│ " : " "), LEFT, result);
}
return result;
}
#Override
public String toString() {
return str("", NO, new StringBuilder()).toString();
}
/**
* Entry point of program (used for testing).
* Valid commands are:
* <pre>
* a (add) item(s) - calls add with each item
* f (find) item - calls search with item
* p (print) - calls toString
* h (height) - calls height
* s (size) - calls size
* sa (sizeabove) low - calls sizeAbove(low)
* sb (sizebelow) high - calls sizeBelow(high)
* si (sizeinbetween) low high - calls sizeBetween(low,high)
* </pre>
* Return values of methods are printed to stdout.
*
* #param args command line arguments are not used.
*/
public static void main(String[] args) {
LinkedBST<String> tree = new LinkedBST<>();
Scanner input = new Scanner(System.in);
while (input.hasNextLine()) {
Scanner line = new Scanner(input.nextLine());
if (line.hasNext()) {
String command = line.next();
switch (command) {
case "a": case "add":
while (line.hasNext()) {
tree.add(line.next());
}
break;
case "f": case "find":
if (line.hasNext()) {
System.out.println(tree.search(line.next()));
}
break;
case "p": case "print":
System.out.print(tree);
break;
case "h": case "height":
System.out.println(tree.height());
break;
case "s": case "size":
System.out.println(tree.size());
break;
case "sa": case "sizeabove":
if (line.hasNext()) {
String low = line.next();
System.out.println(tree.sizeAbove(low));
}
break;
case "sb": case "sizebelow":
if (line.hasNext()) {
System.out.println(tree.sizeBelow(line.next()));
}
break;
case "si": case "sizeinbetween":
if (line.hasNext()) {
String low = line.next();
if (line.hasNext()) {
System.out.println(tree.sizeBetween
(low, line.next()));
}
}
break;
default:
System.err.println("Unknown command: " + command);
}
}
}
}
}
System.out.println(tree.sizeBelow(line.next()));
}
break;
case "si": case "sizeinbetween":
if (line.hasNext()) {
String low = line.next();
if (line.hasNext()) {
System.out.println(tree.sizeBetween
(low, line.next()));
}
}
break;
default:
System.err.println("Unknown command: " + command);
}
}
}
}
}
As this is homework I will try to point you in the right direction rather than do it for you. The task at hand is better solved with recursion and when it comes to binary trees, there are several different types of traversals that can be done recursively.
In-order traversal (LVR)
Reverse order traversal (RVL)
Preorder traversal (VLR)
Postorder traversal (LRV)
I would perform an In-order traversal and increment accordingly if we find any value below high.
Hint:
you'll need to create an inOrder method which takes an argument of the root and an argument of T high and recursively traverse down the tree checking if the current node value is less that high.
public int sizeBelow(T high) {
// return inOrder(root,high);
}
private int inOrder(type current, type high){
// check if ANY of root or high are null (if yes return 0)
// recursively go down the tree comparing current against high
// if current is less than high then return 1 + inOrder(...,high)
// all other conditions should return 0.
}
Ensure you read on Tree Traversals (Inorder, Preorder and Postorder). When you click on this link ensure you select the JAVA tab because by default the examples are shown in C.
Related
I have written a code of Binary Search tree that extends comparable and implements an interface. The code for leaves and the helper method countLeaves (included down here), makes sure that all of the test goes through except for one, heightIsLogOfNumLeavesTreeIsPerfect().
// TODO: Look at the Leaves and the helper method to leaves and see how I should change it so that this test goes through aswell.
//EDIT: I have added the whole tree class
java.lang.AssertionError:
Expected: <2>
but: was <0>
Expected :<2>
Actual :<0>
import org.junit.Test;
import org.junit.Before;
import org.junit.Rule;
import org.junit.rules.Timeout;
import static org.junit.Assert.*;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.CoreMatchers.*;
import java.util.Arrays;
import java.util.stream.IntStream;
/**
* Test class for a tree.
*/
public class TreeTest{
#Rule public Timeout globalTimeout = Timeout.seconds(5);
Tree<Integer> tree;
int[] elementsInTree;
int[] elementsNotInTree;
#Before
public void setUp() {
/**
* This tree should look like this:
*
* 8
* / \
* 3 10
* / \ \
* 1 6 14
* / \ /
* 4 7 13
*/
tree = new Tree<>();
elementsInTree = new int[] {8, 10, 14, 13, 3, 1, 6, 4, 7};
for (int elem : elementsInTree) {
tree.insert(elem);
}
elementsNotInTree = new int[] {34, -3, -10, 12, 74, 5};
}
#Test
public void heightIsLogOfNumLeavesTreeIsPerfect() {
// For a perfect tree, tree.height() == log2(tree.leaves()
// Arrange
Tree<Integer> tree = new Tree<>();
int[] elements = new int[] {8, 3, 10, 1, 6, 9, 14};
int numLeaves = 4;
int logNumLeaves = (int) Math.round(Math.log(numLeaves) / Math.log(2));
for (int elem : elements) {
tree.insert(elem);
}
// Act
int height = tree.height();
// Assert
assertThat(height, equalTo(logNumLeaves));
}
}
**
* An interface describing a generic Comparable
*/
public interface BSTInterface <T>{
boolean search(T elem);
boolean insert(T elem);
int size();
int height();
int leaves();
}
/**
* An Binary Search tree implementation of the comparable interface.
* #param <T>
*/
public class Tree <T extends Comparable <T>> implements BSTInterface <T>{
private int size;
private Node root;
public class Node{
private Node Left;
private Node Right;
private T data;
public Node(T data){
this.data = data;
}
public Node getRight(){
return Right;
}
public Node getLeft() {
return Left;
}
public T getData() {
return data;
}
}
public Tree (){
size = 0;
root = null;
}
/**
* Test for presence of a value.
* #param elem
* #return true/false
*/
#Override
public boolean search(T elem) {
if(root == null ||elem == null){
return false;
}
Node node = root;
while(true){
if(node.data.compareTo(elem) > 0){
if(node.Right == null){
return false;
} else{
node = node.Right;
}
} else if(node.data.compareTo(elem) == 0){
break;
} else{
if(node.Left== null){
return false;
}
else{
node = node.Left;
}
}
}
return true;
}
/**
* Add value to tree; duplicates are not allowed.
* Return true if the element is not already present (and is thus inserted),
* false otherwise.
*
* #param elem
* #return true/false
*/
#Override
public boolean insert(T elem) {
if (elem == null){
return false;
}
if (root == null){
root = new Node(elem);
size++;
return true;
}
Node node = root;
while (true){
if (node.data.compareTo(elem) > 0) {
if (node.Right == null){
node.Right = new Node(elem);
size++;
break;
} else {
node = node.Right;
}
} else if (node.data.compareTo(elem) == 0) {
return false;
} else {
if (node.Left == null){
node.Left = new Node(elem);
size++;
break;
} else {
node = node.Left;
}
}
}
return true;
}
/**
* the number of elements in the tree
* #return size.
*/
#Override
public int size() {
return size;
}
/**
* The height of the tree.
* The empty tree and the tree with only the root node both have height 0.
* #return the height of the tree.
*/
#Override
public int height() {
return countHeight(root);
}
/**
* Helper method for height
*/
private int countHeight(Node node){
if(node == null) {
return 0;
}
return Math.max(countHeight(node.Left), countHeight(node.Right));
}
/**
* The number of leaves in the tree.
* #return the amount of leaves the tree have.
*/
#Override
public int leaves() {
return countLeaves(root);
}
/**
* Helper method for leaves
*/
private int countLeaves(Node node) {
if (node == null) {
return 0;
}
if (node.Left == null && node.Right == null) {
return 1;
}
return countLeaves(node.Left) + countLeaves(node.Right);
}
/**
* A string describing the tree
* #return
*/
public String toString(){
String str = "[" + helpToString(root);
if (str.length() > 1) {
str = str.substring(0, str.length() - 2);
} return str + "]";
}
/**
* Helper method for toString
*/
private String helpToString(Node node) {
String str = "";
if (node != null) {
str += helpToString(node.Right);
str += node.data + ", ";
str += helpToString(node.Left);
}
return str;
}
}
Your countHeight method can return either zero or a maximum of two returns, and there are no other operations in it. You can't get a value greater of zero out of zeros just by using max function.
You should return -1 for an empty tree and max(height(left) + 1, height(right) + 1) otherwise. Each +1 accounts for length of corresponding parent-child edge.
(Mathematically speaking, height of an "empty" tree is usually undefined, but defining it as -1 in this case helps a lot so let's stick to that.)
I have implemented a Binary search tree and I have written a test class (in JUNIT testing) for it. All the test except for one test goes through. The test leavesIsCorrectWhenTreeIsPerfect() gets a message when I debug the code.
Expected: <4> but: was <5> . I do not know have to fix this.
Keep in mind that all the other tests goes through, and I do not think that it is a problem with the tree code.
How do you understand the description for the test?
/**
* A Binary Search tree.
*/
public class Tree <T extends Comparable <T>> implements BinaryTree <T>{
private int size;
private Node root;
public class Node{
private Node Left;
private Node Right;
private T data;
public Node(T data){
this.data = data;
}
}
public Tree (){
size = 0;
root = null;
}
/**
* Test for presence of a value.
* #param elem
* #return true/false
*/
#Override
public boolean search(T elem) {
if(root == null ||elem == null){
return false;
}
Node node = root;
while(true){
if(node.data.compareTo(elem) > 0){
if(node.Right == null){
return false;
} else{
node = node.Right;
}
} else if(node.data.compareTo(elem) == 0){
break;
} else{
if(node.Left== null){
return false;
}
else{
node = node.Left;
}
}
}
return true;
}
/**
* Add value to tree; duplicates are not allowed.
* Return true if the element is not already present (and is thus inserted),
* false otherwise.
*
* #param elem
* #return true/false
*/
#Override
public boolean insert(T elem) {
if (elem == null){
return false;
}
if (root == null){
root = new Node(elem);
size++;
return true;
}
Node node = root;
while (true){
if (node.data.compareTo(elem) > 0) {
if (node.Right == null){
node.Right = new Node(elem);
size++;
break;
} else {
node = node.Right;
}
} else if (node.data.compareTo(elem) == 0) {
return false;
} else {
if (node.Left == null){
node.Left = new Node(elem);
size++;
break;
} else {
node = node.Left;
}
}
}
return true;
}
/**
* The number of elements in the tree
* #return size.
*/
#Override
public int size() {
return size;
}
/**
* The height of the tree.
* The empty tree and the tree with only the root node both have height 0.
* #return the height of the tree.
*/
#Override
public int height() {
return countHeight(root);
}
/**
* Helper method for height
*/
private int countHeight(Node node){
if(node == null) {
return 0;
}
if (node.Left == null && node.Right == null) {
return 0;
}
return 1 + Math.max(countHeight(node.Left), countHeight(node.Right));
}
/**
* The number of leaves in the tree.
* #return the amount of leaves the tree have.
*/
#Override
public int leaves() {
return countLeaves(root);
}
/**
* Helper method for leaves
*/
private int countLeaves(Node node) {
if (node == null) {
return 0;
}
if (node.Left == null && node.Right == null) {
return 1;
}
return countLeaves(node.Left) + countLeaves(node.Right);
}
/**
* A string describing the tree
* #return
*/
public String toString(){
String str = "[" + helpToString(root);
if (str.length() > 1) {
str = str.substring(0, str.length() - 2);
} return str + "]";
}
/**
* Helper method for toString
*/
private String helpToString(Node node) {
String str = "";
if (node != null) {
str += helpToString(node.Right);
str += node.data + ", ";
str += helpToString(node.Left);
}
return str;
}
}
Test class:
import org.junit.Test;
import org.junit.Before;
import org.junit.Rule;
import org.junit.rules.Timeout;
import static org.junit.Assert.*;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.CoreMatchers.*;
import java.util.Arrays;
import java.util.stream.IntStream;
/**
* Test class for a tree.
*/
public class TreeTest{
#Rule public Timeout globalTimeout = Timeout.seconds(5);
Tree<Integer> tree;
int[] elementsInTree;
int[] elementsNotInTree;
#Before
public void setUp() {
/**
* This tree should look like this:
*
* 8
* / \
* 3 10
* / \ \
* 1 6 14
* / \ /
* 4 7 13
*/
tree = new Tree<>();
elementsInTree = new int[] {8, 10, 14, 13, 3, 1, 6, 4, 7};
for (int elem : elementsInTree) {
tree.insert(elem);
}
elementsNotInTree = new int[] {34, -3, -10, 12, 74, 5};
}
#Test
public void leavesIsTwoWhenPerfectTreeHasThreeNodes() {
// Arrange
Tree<Integer> tree = new Tree<>();
// root must be smaller than one and larger than the other child
tree.insert(1338); // root
tree.insert(1337); // smaller child
tree.insert(1396); // larger child
// Act
int numLeaves = tree.leaves();
// Assert
assertThat(numLeaves, equalTo(2));
}
#Test
public void leavesIsCorrectWhenTreeIsPerfect() { //TEST
// A perfect tree has all leaves at the same depth, and all internal nodes
// (i.e. non-leaves) have two children
//
// This test should assert that a perfect tree with 2*n-1 nodes total,
// has exactly n leaves (i.e. that Tree.leaves() returns n).
//
// An example is the perfect three-node tree from the test above:
//
// (1338)
// / \
// (1337) (1396)
// You have to construct our own tree here, with n >= 4
int n = 4;
int nodes = 2*n-1;
for(int i = 0; i < nodes ; i++) {
tree.insert(i);
}
int leaves = tree.leaves();
assertThat(leaves,equalTo(n));
}
#Test
public void leavesIsOneWhenElementsWereInsertedInAscendingOrder() {
// Arrange
Tree<Integer> tree = new Tree<>();
// insert elements in ascending order => all elements are inserted to the right
int numElements = 100;
for (int i = 0; i < numElements; i++) {
tree.insert(i);
}
// Act
int numLeaves = tree.leaves();
// Assert
assertThat(numLeaves, equalTo(1));
}
// Tests for height
#Test
public void heightIsZeroWhenTreeIsEmpty() {
// Arrange
Tree<Integer> emptyTree = new Tree<>();
// Act
int height = emptyTree.height();
// Assert
assertThat(height, equalTo(0));
}
#Test
public void heightIsLogOfNumLeavesTreeIsPerfect() {
// For a perfect tree, tree.height() == log2(tree.leaves()
// Arrange
Tree<Integer> tree = new Tree<>();
int[] elements = new int[] {8, 3, 10, 1, 6, 9, 14};
int numLeaves = 4;
int logNumLeaves = (int) Math.round(Math.log(numLeaves) / Math.log(2));
for (int elem : elements) {
tree.insert(elem);
}
// Act
int height = tree.height();
// Assert
assertThat(height, equalTo(logNumLeaves));
}
// Tests for insert/height
#Test
public void insertValuesInAscendingOrderIncrementsHeight() {
// When inserting elements in ascending order, each element is inserted
// to the right of the deepest node, so the height should increment by
// 1 for each element inserted.
Tree <Integer> tree = new Tree<>();
int val = 100;
for(int i = 0; i < val; i++){
tree.insert(i);
}
int treeHeight = tree.height();
treeHeight++;
assertThat(val,equalTo(treeHeight));
}
}
The leaves method counts the number of leaves that you have. Your expectation is that there will be four leaves. However, the result is 5. The method that you use in order to count the leaves appears to be correct:
/**
* Helper method for leaves
*/
private int countLeaves(Node node) {
if (node == null) {
return 0;
}
if (node.Left == null && node.Right == null) {
return 1;
}
return countLeaves(node.Left) + countLeaves(node.Right);
}
It essentially counts the items (in a breadth-first-search fashion) that have no children. What could be the reason? Your main thing to find out how your tree looks alike. Based on your insert method:
#Override
public boolean insert(T elem) {
if (elem == null){
return false;
}
if (root == null){
root = new Node(elem);
size++;
return true;
}
Node node = root;
while (true){
if (node.data.compareTo(elem) > 0) {
if (node.Right == null){
node.Right = new Node(elem);
size++;
break;
} else {
node = node.Right;
}
} else if (node.data.compareTo(elem) == 0) {
return false;
} else {
if (node.Left == null){
node.Left = new Node(elem);
size++;
break;
} else {
node = node.Left;
}
}
}
return true;
}
and knowing that 2*n-1 in your test is 7, so the input numbers are 0, 1, 2, 3, 4, 5, 6, it seems to me that the tree could be
0
/
1
/
2
/
3
/
4
/
5
/
6
which would have a single leaf, because if the node has a greater value than the item you are about to insert, then you add it to the node's right. However, the results of your test are different. For some reason you have 5 leaves. You would need to check the reason of that by first finding out how your table looks alike at the end and second by looking into the insert method with a debugger for this input.
I look for a queue that stores up to N elements for a certain time (i.e. 10 sec) or should dispose the oldest value if full.
I found a similar queue in the Apache Collections (CircularFifoQueue JavaDoc) that misses the aspect of time to live. A full fletched message broker seems like an overhead for my small project.
Do you mind giving me a hint how I should implement this? Shall I filter out old values while I poll for elements?
There is a class called LinkedHashMap which has a special method for removing stale data. From the documentation:
protected boolean removeEldestEntry(Map.Entry eldest)
Returns true if this map should remove its eldest entry.
The method removeEldestEntry is called whenever anything is added to the list (queue). If it returns true then the eldest entry is removed to make room for the new entry, otherwise nothing is removed. You can add your own implementation which checks the timestamp on the eldest entry and returns true if it be older than a threshhold for expiration (e.g. 10 seconds). So your implementation might look something like this:
protected boolean removeEldestEntry(Map.Entry eldest) {
long currTimeMillis = System.currentTimeMillis();
long entryTimeMillis = eldest.getValue().getTimeCreation();
return (currTimeMillis - entryTimeMillis) > (1000*10*60);
}
I think java.util.LinkedHashMap is the solution for you. It has a removeEldest() method which is called whenever an entry is put in the map. You can override it to indicate if the eldest entry should be removed.
The JavaDoc gives a good example:
private static final int MAX_ENTRIES = 100;
protected boolean removeEldestEntry(Map.Entry eldest) {
return size() > MAX_ENTRIES;
}
This removes the eldest entry if the map has more than 100 entries.
Pro-actively removing items after 10 seconds would require a separate thread to check age and remove old items. I am guessing this is not what you want, judging by your description.
I used to following queue implementation. The code is heavily based on Apaches CircularFifoQueue and is only weakly tested. Moreover the implementation is not thread-safe and not serializable.
Leave a comment if you spot a mistake.
import java.util.AbstractCollection;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Queue;
import java.util.concurrent.TimeUnit;
/**
* TimedQueue is a first-in first-out queue with a fixed size that
* replaces its oldest element if full.
* <p>
* The removal order of a {#link TimedQueue} is based on the
* insertion order; elements are removed in the same order in which they
* were added. The iteration order is the same as the removal order.
* <p>
* The {#link #add(Object)}, {#link #remove()}, {#link #peek()}, {#link #poll},
* {#link #offer(Object)} operations all perform in constant time.
* All other operations perform in linear time or worse.
* <p>
* This queue prevents null objects from being added and it is not thread-safe and not serializable.
*
* The majority of this source code was copied from Apaches {#link CircularFifoQueue}: http://commons.apache.org/proper/commons-collections/apidocs/org/apache/commons/collections4/queue/CircularFifoQueue.html
*
* #version 1.0
*/
public class TimedQueue<E> extends AbstractCollection<E>
implements Queue<E> {
/** Underlying storage array. */
private Item<E>[] elements;
/** Array index of first (oldest) queue element. */
private int start = 0;
/**
* Index mod maxElements of the array position following the last queue
* element. Queue elements start at elements[start] and "wrap around"
* elements[maxElements-1], ending at elements[decrement(end)].
* For example, elements = {c,a,b}, start=1, end=1 corresponds to
* the queue [a,b,c].
*/
private transient int end = 0;
/** Flag to indicate if the queue is currently full. */
private transient boolean full = false;
/** Capacity of the queue. */
private final int maxElements;
private TimeUnit unit;
private int delay;
/**
* Constructor that creates a queue with the default size of 32.
*/
public TimedQueue() {
this(32);
}
/**
* Constructor that creates a queue with the specified size.
*
* #param size the size of the queue (cannot be changed)
* #throws IllegalArgumentException if the size is < 1
*/
public TimedQueue(final int size) {
this(size, 3, TimeUnit.SECONDS);
}
#SuppressWarnings("unchecked")
public TimedQueue(final int size, int delay, TimeUnit unit) {
if (size <= 0) {
throw new IllegalArgumentException("The size must be greater than 0");
}
elements = new Item[size];
maxElements = elements.length;
this.unit = unit;
this.delay = delay;
}
/**
* Constructor that creates a queue from the specified collection.
* The collection size also sets the queue size.
*
* #param coll the collection to copy into the queue, may not be null
* #throws NullPointerException if the collection is null
*/
public TimedQueue(final Collection<? extends E> coll) {
this(coll.size());
addAll(coll);
}
/**
* Returns the number of elements stored in the queue.
*
* #return this queue's size
*/
#Override
public int size() {
int size = 0;
for(int i = 0; i < elements.length; i++) {
if(validElement(i) != null) {
size++;
}
}
return size;
}
/**
* Returns true if this queue is empty; false otherwise.
*
* #return true if this queue is empty
*/
#Override
public boolean isEmpty() {
return size() == 0;
}
private boolean isAtFullCapacity() {
return size() == maxElements;
}
/**
* Clears this queue.
*/
#Override
public void clear() {
full = false;
start = 0;
end = 0;
Arrays.fill(elements, null);
}
/**
* Adds the given element to this queue. If the queue is full, the least recently added
* element is discarded so that a new element can be inserted.
*
* #param element the element to add
* #return true, always
* #throws NullPointerException if the given element is null
*/
#Override
public boolean add(final E element) {
if (null == element) {
throw new NullPointerException("Attempted to add null object to queue");
}
if (isAtFullCapacity()) {
remove();
}
elements[end++] = new Item<E>(element);
if (end >= maxElements) {
end = 0;
}
if (end == start) {
full = true;
}
return true;
}
/**
* Returns the element at the specified position in this queue.
*
* #param index the position of the element in the queue
* #return the element at position {#code index}
* #throws NoSuchElementException if the requested position is outside the range [0, size)
*/
public E get(final int index) {
final int sz = size();
if (sz == 0) {
throw new NoSuchElementException(
String.format("The specified index (%1$d) is outside the available range because the queue is empty.", Integer.valueOf(index)));
}
if (index < 0 || index >= sz) {
throw new NoSuchElementException(
String.format("The specified index (%1$d) is outside the available range [0, %2$d]",
Integer.valueOf(index), Integer.valueOf(sz-1)));
}
final int idx = (start + index) % maxElements;
return validElement(idx);
}
private E validElement(int idx) {
if(elements[idx] == null){
return null;
}
long diff = System.currentTimeMillis() - elements[idx].getCreationTime();
if(diff < unit.toMillis(delay)) {
return (E) elements[idx].getValue();
} else {
elements[idx] = null;
return null;
}
}
//-----------------------------------------------------------------------
/**
* Adds the given element to this queue. If the queue is full, the least recently added
* element is discarded so that a new element can be inserted.
*
* #param element the element to add
* #return true, always
* #throws NullPointerException if the given element is null
*/
public boolean offer(E element) {
return add(element);
}
public E poll() {
if (isEmpty()) {
return null;
}
return remove();
}
public E element() {
if (isEmpty()) {
throw new NoSuchElementException("queue is empty");
}
return peek();
}
public E peek() {
if (isEmpty()) {
return null;
}
return (E) elements[start].getValue();
}
public E remove() {
if (isEmpty()) {
throw new NoSuchElementException("queue is empty");
}
final E element = validElement(start);
if (null != element) {
elements[start++] = null;
if (start >= maxElements) {
start = 0;
}
full = false;
}
return element;
}
/**
* Increments the internal index.
*
* #param index the index to increment
* #return the updated index
*/
private int increment(int index) {
index++;
if (index >= maxElements) {
index = 0;
}
return index;
}
/**
* Decrements the internal index.
*
* #param index the index to decrement
* #return the updated index
*/
private int decrement(int index) {
index--;
if (index < 0) {
index = maxElements - 1;
}
return index;
}
/**
* Returns an iterator over this queue's elements.
*
* #return an iterator over this queue's elements
*/
#Override
public Iterator<E> iterator() {
return new Iterator<E>() {
private int index = start;
private int lastReturnedIndex = -1;
private boolean isFirst = full;
public boolean hasNext() {
return (isFirst || index != end) && size() > 0;
}
public E next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
isFirst = false;
lastReturnedIndex = index;
index = increment(index);
if(validElement(lastReturnedIndex) == null) {
return next();
} else {
return validElement(lastReturnedIndex);
}
}
public void remove() {
if (lastReturnedIndex == -1) {
throw new IllegalStateException();
}
// First element can be removed quickly
if (lastReturnedIndex == start) {
TimedQueue.this.remove();
lastReturnedIndex = -1;
return;
}
int pos = lastReturnedIndex + 1;
if (start < lastReturnedIndex && pos < end) {
// shift in one part
System.arraycopy(elements, pos, elements, lastReturnedIndex, end - pos);
} else {
// Other elements require us to shift the subsequent elements
while (pos != end) {
if (pos >= maxElements) {
elements[pos - 1] = elements[0];
pos = 0;
} else {
elements[decrement(pos)] = elements[pos];
pos = increment(pos);
}
}
}
lastReturnedIndex = -1;
end = decrement(end);
elements[end] = null;
full = false;
index = decrement(index);
}
};
}
private static final class Item<E> {
private long creationTime;
private E in;
public Item(E in) {
this.in = in;
creationTime = System.currentTimeMillis();
}
public E getValue() {
return in;
}
public long getCreationTime() {
return creationTime;
}
}
}
I am working on zk tree by extending DefaultTreeModel. it's properly renders on webpage. But when I try to get the model associated with this tree using root, i am getting null.
I am using two ways to do that.
1. using Tree hierarchyTree; hierarchyTree.getModel().getRoot(); // returns null
2. using AdvancedTreeModel contactTreeModel extends DefaultTreeModel;
ContactTreeNode nodes2 = (ContactTreeNode) contactTreeModel.getRoot(); //also return null
Can you plz, tell me how can I get the root node, or model associate with it, which is not null. So that I can iterate over it.
AdvanceTeeModel.java
package demo.tree.dynamic_tree;
import org.zkoss.zul.DefaultTreeModel;
import org.zkoss.zul.DefaultTreeNode;
import demo.data.pojo.Contact;
public class AdvancedTreeModel extends DefaultTreeModel<Contact> {
private static final long serialVersionUID = -5513180500300189445L;
DefaultTreeNode<Contact> _root;
public AdvancedTreeModel(ContactTreeNode contactTreeNode) {
super(contactTreeNode);
_root = contactTreeNode;
}
/**
* remove the nodes which parent is <code>parent</code> with indexes
* <code>indexes</code>
*
* #param parent
* The parent of nodes are removed
* #param indexFrom
* the lower index of the change range
* #param indexTo
* the upper index of the change range
* #throws IndexOutOfBoundsException
* - indexFrom < 0 or indexTo > number of parent's children
*/
public void remove(DefaultTreeNode<Contact> parent, int indexFrom, int indexTo) throws IndexOutOfBoundsException {
DefaultTreeNode<Contact> stn = parent;
for (int i = indexTo; i >= indexFrom; i--)
try {
stn.getChildren().remove(i);
} catch (Exception exp) {
exp.printStackTrace();
}
}
public void remove(DefaultTreeNode<Contact> target) throws IndexOutOfBoundsException {
int index = 0;
DefaultTreeNode<Contact> parent = null;
// find the parent and index of target
parent = dfSearchParent(_root, target);
if(parent!=null){
for (index = 0; index < parent.getChildCount(); index++) {
if (parent.getChildAt(index).equals(target)) {
break;
}
}
remove(parent, index, index);
}
}
/**
* insert new nodes which parent is <code>parent</code> with indexes
* <code>indexes</code> by new nodes <code>newNodes</code>
*
* #param parent
* The parent of nodes are inserted
* #param indexFrom
* the lower index of the change range
* #param indexTo
* the upper index of the change range
* #param newNodes
* New nodes which are inserted
* #throws IndexOutOfBoundsException
* - indexFrom < 0 or indexTo > number of parent's children
*/
public void insert(DefaultTreeNode<Contact> parent, int indexFrom, int indexTo, DefaultTreeNode<Contact>[] newNodes)
throws IndexOutOfBoundsException {
DefaultTreeNode<Contact> stn = parent;
for (int i = indexFrom; i <= indexTo; i++) {
try {
stn.getChildren().add(i, newNodes[i - indexFrom]);
} catch (Exception exp) {
throw new IndexOutOfBoundsException("Out of bound: " + i + " while size=" + stn.getChildren().size());
}
}
}
/**
* append new nodes which parent is <code>parent</code> by new nodes
* <code>newNodes</code>
*
* #param parent
* The parent of nodes are appended
* #param newNodes
* New nodes which are appended
*/
public void add(DefaultTreeNode<Contact> parent, DefaultTreeNode<Contact>[] newNodes) {
DefaultTreeNode<Contact> stn = (DefaultTreeNode<Contact>) parent;
for (int i = 0; i < newNodes.length; i++)
stn.getChildren().add(newNodes[i]);
}
private DefaultTreeNode<Contact> dfSearchParent(DefaultTreeNode<Contact> node, DefaultTreeNode<Contact> target) {
if (node.getChildren() != null && node.getChildren().contains(target)) {
return node;
} else {
int size = getChildCount(node);
for (int i = 0; i < size; i++) {
DefaultTreeNode<Contact> parent = dfSearchParent((DefaultTreeNode<Contact>) getChild(node, i), target);
if (parent != null) {
return parent;
}
}
}
return null;
}
}
Actually I am using this example,
[http://www.zkoss.org/zkdemo/tree/dynamic_tree][Dynamic Tree from ZK]
And in Composer's override render method i am using following line of code to get the root/model from the tree.
ContactTreeNode nodes = (ContactTreeNode) hierarchyTree.getModel().getRoot();
System.out.println("nodes: " + nodes);
ContactTreeNode nodes2 = (ContactTreeNode) contactTreeModel.getRoot();
Your remove method have to care about removing the root.
Means, if you remove the root, you have to set a new one.
Try if is solves the problem. Or try if the problem occurs
right after initialization of (and without editing) the tree.
I've been trying to write a recursive string method for a binary search tree that returns a multiple line representation of a tree with preorder path info.
Each node should be prefaced by a series of < and > characters showing the path that leads from the root to that node. I'm not sure how to use a string prefix parameter that is extended by one character with each successive call.
The method should be able reproduce this example:
Tree:
15
/ \
12 18
/ / \
10 16 20
\ \
11 17
Expected Print Output:
15
<12
<<10
<<>11
>18
><16
><>17
>>20
I'm new to recursion and so far my actual print output isn't close enough after hours of messing with the code:
18
<17
<10
>15
<11
>12
16
20
Here's my tree node class that works properly:
/**
* A single binary tree node.
* <p>
* Each node has both a left or right child, which can be null.
*/
public class TreeNode<E> {
private E data;
private TreeNode<E> left;
private TreeNode<E> right;
/**
* Constructs a new node with the given data and references to the
* given left and right nodes.
*/
public TreeNode(E data, TreeNode<E> left, TreeNode<E> right) {
this.data = data;
this.left = left;
this.right = right;
}
/**
* Constructs a new node containing the given data.
* Its left and right references will be set to null.
*/
public TreeNode(E data) {
this(data, null, null);
}
/** Returns the item currently stored in this node. */
public E getData() {
return data;
}
/** Overwrites the item stored in this Node with the given data item. */
public void setData(E data) {
this.data = data;
}
/**
* Returns this Node's left child.
* If there is no left left, returns null.
*/
public TreeNode<E> getLeft() {
return left;
}
/** Causes this Node to point to the given left child Node. */
public void setLeft(TreeNode<E> left) {
this.left = left;
}
/**
* Returns this nodes right child.
* If there is no right child, returns null.
*/
public TreeNode<E> getRight() {
return right;
}
/** Causes this Node to point to the given right child Node. */
public void setRight(TreeNode<E> right) {
this.right = right;
}
}
Here's my binary search tree class with the toFullString() method near the bottom:
import java.util.*;
/**
* A binary search tree (BST) is a sorted ADT that uses a binary
* tree to keep all elements in sorted order. If the tree is
* balanced, performance is very good: O(n lg n) for most operations.
* If unbalanced, it performs more like a linked list: O(n).
*/
public class BinarySearchTree<E extends Comparable<E>> {
private TreeNode<E> root = null;
private int size = 0;
/** Creates an empty tree. */
public BinarySearchTree() {
}
public BinarySearchTree(Collection<E> col) {
List<E> list = new ArrayList<E>(col);
Collections.shuffle(list);
for (int i = 0; i < list.size() ; i++) {
add(list.get(i));
}
}
/** Adds the given item to this BST. */
public void add(E item) {
this.size++;
if (this.root == null) {
//tree is empty, so just add item
this.root = new TreeNode<E>(item);
}else {
//find where to insert, with pointer to parent node
TreeNode<E> parent = null;
TreeNode<E> curr = this.root;
boolean wentLeft = true;
while (curr != null) { //will execute at least once
parent = curr;
if (item.compareTo(curr.getData()) <= 0) {
curr = curr.getLeft();
wentLeft = true;
}else {
curr = curr.getRight();
wentLeft = false;
}
}
//now add new node on appropriate side of parent
curr = new TreeNode<E>(item);
if (wentLeft) {
parent.setLeft(curr);
}else {
parent.setRight(curr);
}
}
}
/** Returns the greatest (earliest right-most node) of the given tree. */
private E findMax(TreeNode<E> n) {
if (n == null) {
return null;
}else if (n.getRight() == null) {
//can't go right any more, so this is max value
return n.getData();
}else {
return findMax(n.getRight());
}
}
/**
* Returns item from tree that is equivalent (according to compareTo)
* to the given item. If item is not in tree, returns null.
*/
public E get(E item) {
return get(item, this.root);
}
/** Finds it in the subtree rooted at the given node. */
private E get(E item, TreeNode<E> node) {
if (node == null) {
return null;
}else if (item.compareTo(node.getData()) < 0) {
return get(item, node.getLeft());
}else if (item.compareTo(node.getData()) > 0) {
return get(item, node.getRight());
}else {
//found it!
return node.getData();
}
}
/**
* Removes the first equivalent item found in the tree.
* If item does not exist to be removed, throws IllegalArgumentException().
*/
public void remove(E item) {
this.root = remove(item, this.root);
}
private TreeNode<E> remove(E item, TreeNode<E> node) {
if (node == null) {
//didn't find item
throw new IllegalArgumentException(item + " not found in tree.");
}else if (item.compareTo(node.getData()) < 0) {
//go to left, saving resulting changes made to left tree
node.setLeft(remove(item, node.getLeft()));
return node;
}else if (item.compareTo(node.getData()) > 0) {
//go to right, saving any resulting changes
node.setRight(remove(item, node.getRight()));
return node;
}else {
//found node to be removed!
if (node.getLeft() == null && node.getRight() == null) {
//leaf node
return null;
}else if (node.getRight() == null) {
//has only a left child
return node.getLeft();
}else if (node.getLeft() == null) {
//has only a right child
return node.getRight();
}else {
//two children, so replace the contents of this node with max of left tree
E max = findMax(node.getLeft()); //get max value
node.setLeft(remove(max, node.getLeft())); //and remove its node from tree
node.setData(max);
return node;
}
}
}
/** Returns the number of elements currently in this BST. */
public int size() {
return this.size;
}
/**
* Returns a single-line representation of this BST's contents.
* Specifically, this is a comma-separated list of all elements in their
* natural Comparable ordering. The list is surrounded by [] characters.
*/
#Override
public String toString() {
return "[" + toString(this.root) + "]";
}
private String toString(TreeNode<E> n) {
//would have been simpler to use Iterator... but not implemented yet.
if (n == null) {
return "";
}else {
String str = "";
str += toString(n.getLeft());
if (!str.isEmpty()) {
str += ", ";
}
str += n.getData();
if (n.getRight() != null) {
str += ", ";
str += toString(n.getRight());
}
return str;
}
}
public String toFullString() {
StringBuilder sb = new StringBuilder();
toFullString(root, sb);
return sb.toString();
}
/**
* Preorder traversal of the tree that builds a string representation
* in the given StringBuilder.
* #param n root of subtree to be traversed
* #param sb StringBuilder in which to create a string representation
*/
private void toFullString(TreeNode<E> n, StringBuilder sb)
{
if (n == null)
{
return;
}
sb.append(n.getData().toString());
sb.append("\n");
if (n.getLeft() != null) {
sb.append("<");
} else if (n.getRight() != null) {
sb.append(">");
}
if (n.getLeft() != null || n.getRight() != null)
{
toFullString(n.getLeft(), sb);
toFullString(n.getRight(), sb);
}
}
/**
* Tests the BST.
*/
public static void main(String[] args) {
Collection collection = new ArrayList();
collection.add(15);
collection.add(12);
collection.add(18);
collection.add(10);
collection.add(16);
collection.add(20);
collection.add(11);
collection.add(17);
BinarySearchTree bst = new BinarySearchTree(collection);
//System.out.println(bst);
String temp = bst.toFullString();
System.out.println(temp);
}
}
Any help with the recursive toFullString method will be greatly appreciated.
There are two levels you need to think about when designing a recursive solution.
How do I deal with the current item in the recursion?
How do I go from this item to the next one, and what information gets passed on?
Since we are printing out each item in the tree, there is going to be a single print statement inside of each recursive call. However, the string printed in each row includes information about previous steps that were traversed to reach the current node. How do we deal with this information? It needs to be passed down from previous recursive calls to the next one.
Here is a set of possible rules:
If the current item is a leaf, print out the current result.
If the current item has a left node, add <, and recursively act on the left node.
If the current item has a right node, add >, and recursively act on the right node.
What do we add < and > to? We need a parameter to carry along the current previous steps that have occurred in the recursion from recursive call to call. You don't want to simply print out < and > as you traverse the tree, because you need to remember the current set of steps to print out the prefix to every single node. There could be a second helper method that takes the same parameters as the original toFullString(), but a custom third parameter to carry the current set of previous steps.
So the logic might look something like this:
If there is no current prefix, initialize prefix to "", since the root initially has no steps to reach it.
If the current node is a leaf, add a line of output with the current prefix + leaf value.
If the current node has a left node, recursively call toFullString() on the left node, and add < to the current prefix string, which is handed down.
If the current node has a right node, recursively call toFullString() on the right node, and add > to the current prefix string, which is handed down.