JUNIT test for binary search tree - java

I have implemented a binary search tree. Most of my test in JUNIT test is going through, including these two. I have implementedleavesIsCorrectWhenTreeIsPerfect() and insertValuesInAscendingOrderIncrementsHeight().
Both of these test goes through, however i do not know if it is correctly written based on what their description is asking.
//EDIT: I have added a test that might be a help to one of the test a need help with.
//TODO: help me understand if I have written the correct code for the test in insertValuesInAscendingOrderIncrementsHeight() and leavesIsCorrectWhenTreeIsPerfect() based on what the description for the test is saying.
Have in mind, that I have not included all the tests in the test class, Because the goes through with my implementation of the tree.
Down here I have included my tree class and the test class with both the test I need help with.
/**
* An Binary Search tree implementation.
* #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;
}
if (node.Left == null && node.Right == null) {
return 0;
}
return 1 + Math.max(countHeight(node.getLeft()), countHeight(node.getRight()));
}
/**
* The number of leaves in the tree.
* #return the amount of leaves the tree has.
*/
#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;
}
}
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));
}
#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 your own tree here, with n >= 4
Tree <Integer> tree = new Tree<>();
int n = 4;
for(int i = 0; i>=n; i++) {
tree.insert(i);
int numLeaves = 2*n-1;
int leaves = tree.leaves();
assertThat(leaves,equalTo(numLeaves));
}
}
// Tests for insert/height
#Test
public void insertValuesInAscendingOrderIncrementsHeight() { //TEST
// 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(tree.size(),equalTo(treeHeight));
}
}

for(int i = 0; i>=n; i++) {
tree.insert(i);
The condition of your for loop is always false.

It's awkward that "n" is used in the instructions to describe a tree with n leaves because "n" is traditionally used to describe your node count. But picture a tree with 4 nodes at the bottom, then half that for the prior layer, then half again for the first layer, and you have a tree with 1+2+4 nodes, or 7 nodes total, which coincides with the formula 2*n-1 (2*4-1=7).
#Test
public void leavesIsCorrectWhenTreeIsPerfect() {
int n=4;
int[] balanced=new int[] {4,2,6,1,3,5,7};
for (int i=0; i<balanced.length; i++) {
tree.insert(balanced[i]);
}
int leaves = tree.leaves();
assertThat(balanced.length,equalTo(2*n-1));
assertThat(leaves,equalTo(n));
}

Related

Java sorted linked list printing twice

I am writing a program that will take in a set of data, process it and sort it in a linked list. My issue is that when I look at the output, when my (myList.retrieve(0, 20)); method is called the first time, it prints double what it should. The second time after deleting 2 elements, the output is correct.
public class Lab9Tester {
public static void main(String args[]){
int testData[] = {5, 2, 7, 8, 3, 6, 10, 2, 6};
int numberOfElementsDeleted = 0;
SortedListOfInt myList = new SortedListOfInt();
for (int i = 0; i < testData.length; i++){
myList.addElement(testData[i]);
}
System.out.println("The values in the sorted list are given below");
System.out.println(myList.retrieve(0, 20));
System.out.println("The values in the sorted list between 4 and 7 are given below");
System.out.println(myList.retrieve(4, 7));
numberOfElementsDeleted = myList.deleteElement(6);
System.out.println("Number of deleted elements is " + numberOfElementsDeleted);
System.out.println("The values in the sorted list after deleting all elements with value 6 are given below");
System.out.println(myList.retrieve(0, 20));
}
}
My test program, which when run I expect the ouput:
The values in the sorted list are given below
2 2 3 5 6 6 7 8 10
The values in the sorted list between 4 and 7 are given below
5 6 6 7
Number of deleted elements is 2
The values in the sorted list after deleting all elements with value 6 are given below
2 2 3 5 7 8 10
However the first line of values is printed twice as
2 2 3 5 6 6 7 8 10 2 2 3 5 6 6 7 8 10
My other classes are as follows:
public class SortedListOfInt {
ListGeneral myList = new ListGeneral();
private boolean needToRestart = true;
private boolean restartFlag = false;
public void addElement(int x){
if(myList.listIsEmpty())
{
myList.addAfterCurrent(x);
return;
}
if (myList.endOfList())
{
myList.addBeforeCurrent(x);
myList.restart();
return;
}
if (myList.currentValue() != null){
int currentValue = (int) (myList.currentValue());
if(currentValue >= x)
{
myList.addBeforeCurrent(x);
myList.restart();
}
else if(currentValue < x)
{
myList.getNextNode();
addElement(x);
}
}
}
public String retrieve(int lowerLimit, int upperLimit)
{
if(myList.listIsEmpty())
{
return "";
}
if(myList.endOfList() && needToRestart)
{
myList.restart();
needToRestart = false;
return "" + retrieve(lowerLimit, upperLimit);
}
if(myList.endOfList())
{
needToRestart = true;
return "";
}
int currentValue = (int) (myList.currentValue());
if(currentValue >= lowerLimit && currentValue <= upperLimit)
{
String result =currentValue + " " ;
myList.getNextNode();
return result + retrieve(lowerLimit,upperLimit);
}
else
{
myList.getNextNode();
return retrieve(lowerLimit,upperLimit);
}
}
public int deleteElement(int x)
{
repointToStart();
int currentValue;
if(myList.endOfList())
{
restartFlag = false;
return 0;
}
else
{
currentValue = (int) myList.currentValue();
if(currentValue == x)
{
myList.removeCurrent();
return deleteElement(x) + 1;
}
else
{
myList.getNextNode();
return deleteElement(x);
}
}
}
private void repointToStart()
{
if(restartFlag == false)
{
myList.restart();
restartFlag = true;
}
}
}
Given by the professor:
public class ListGeneral {
protected Node firstNode; // firstNode can be used by this
// class and any of its subclass.
private Node currentNode, previousNode; // These are usable only
// within this class.
public ListGeneral(){ // Constructor creates an
// empty list.
currentNode = null;
firstNode = null;
previousNode = null;
}
/*
* The method addAfterCurrent adds a new node with value x
* after the current node.
*/
public void addAfterCurrent(Object x){
if (firstNode == null){
firstNode = new Node(x, null);
currentNode = firstNode;
}
else{
Node newNode = new Node(x, currentNode.getNext());
currentNode.setNext(newNode);
previousNode = currentNode;
currentNode = newNode;
}
}
/*
* The method addBeforeCurrent adds a new node with value x
* before the current node.
*/
public void addBeforeCurrent(Object x){
if (firstNode == null){
firstNode = new Node(x, null);
currentNode = firstNode;
}
else {
Node newNode = new Node(x, currentNode);
if (previousNode != null) {
previousNode.setNext(newNode);
}
else{
firstNode = newNode;
}
currentNode = newNode;
}
}
/*
* removeCurrent() deletes the current node. This is defined
* only if the list is not empty.
*/
public void removeCurrent(){
Node temp;
if (listIsEmpty() || endOfList()) return;
temp = currentNode.getNext();
/*
* if previousNode is null, firstNode is currentNode.
*/
if (previousNode == null) {
firstNode = temp;
}
else {
previousNode.setNext(temp);
}
currentNode = currentNode.getNext();
}
/*
* listIsEmpty() is true if list is empty.
* current() returns the current node.
* restart() makes the the first node the current node.
*/
public boolean listIsEmpty(){
return firstNode == null;
}
public Object currentValue(){
return currentNode.getValue();
}
public void restart(){
currentNode = firstNode;
previousNode = null;
}
/* endOfList() is true if current is not pointing to
* any node.
*/
public boolean endOfList(){
return currentNode == null;
}
/* getNextNode makes the next node the current node.
* The method returns true if the operation was successful
* otherwise it returns false.
*/
public boolean getNextNode(){
if (currentNode == null) {
return false;
}
else {
previousNode = currentNode;
currentNode = currentNode.getNext();
return true;
}
}
/*
* method toString() returns the result of invoking toString()
* on all successive elements of the list.
*/
public String toString(){
String s = "";
for(restart(); !endOfList(); getNextNode()){
s += currentValue() + "\n";
}
return s;
}
}
As well as given:
public class Node {
private Object value; // self-referential link.
private Node next;
public Node(Object value, Node nextNode) // The constructor inserts the
{ // arguments in the new object.
this.value = value;
this.next = nextNode;
}
public Object getValue(){
return value;
}
public Node getNext(){
return next;
}
public void setValue(Object value){
this.value = value;
}
public void setNext(Node next){
this.next = next;
}
}
The problem is you are setting needToRestart = true in the SortedListofInt class.So when you do the initial retrieve it calls it twice but sets the needToRestart = false. Next time around when you call the retrieve it prints only once. Setting the needToRestart = false initially is working for me and printing out the correct output.
This is working for me
public class SortedListOfInt {
ListGeneral myList = new ListGeneral();
private boolean needToRestart = false;
private boolean restartFlag = false; //the only change
...
}

AutoComplete using a Trie in Java

I am working on this assignment which implements Autocomplete and dictionary. I have sucessfully implemented spellcheck and the addWord() and isWord() functions.
But I am just not able to implement the function which predicts words for AutoCompletions.
package spelling;
import java.util.List;
import java.util.Queue;
import java.util.Set;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
/**
* An trie data structure that implements the Dictionary and the AutoComplete ADT
* #author You
*
*/
public class AutoCompleteDictionaryTrie implements Dictionary, AutoComplete {
private TrieNode root;
private int size;
public AutoCompleteDictionaryTrie()
{
root = new TrieNode();
size=0;
}
/** Insert a word into the trie.
* For the basic part of the assignment (part 2), you should ignore the word's case.
* That is, you should convert the string to all lower case as you insert it. */
public boolean addWord(String word)
{
//TODO: Implement this method.
String Word=word.toLowerCase();
if(isWord(Word))
return false;
HashMap<Character, TrieNode> children=root.children;
for(int i=0; i<Word.length(); i++){
char c = Word.charAt(i);
TrieNode t;
if(children.containsKey(c)){
t = children.get(c);
}else{
t = new TrieNode(""+(c));
children.put(c, t);
}
children = t.children;
if(i==Word.length()-1)
{
t.isWord = true;
size++;
}
}
return true;
}
/**
* Return the number of words in the dictionary. This is NOT necessarily the same
* as the number of TrieNodes in the trie.
*/
public int size()
{
//TODO: Implement this method
return size;
}
/** Returns whether the string is a word in the trie */
#Override
public boolean isWord(String s)
{
// TODO: Implement this method
TrieNode t = searchNode(s.toLowerCase());
if(t != null && t.isWord)
return true;
else
return false;
}
public TrieNode searchNode(String str){
HashMap<Character, TrieNode> children = root.children;
TrieNode t = null;
for(int i=0; i<str.length(); i++){
char c = str.charAt(i);
if(children.containsKey(c)){
t = children.get(c);
children = t.children;
}else{
return null;
}
}
return t;
}
/**
* * Returns up to the n "best" predictions, including the word itself,
* in terms of length
* If this string is not in the trie, it returns null.
* #param text The text to use at the word stem
* #param n The maximum number of predictions desired.
* #return A list containing the up to n best predictions
*/#Override
public List<String> predictCompletions(String prefix, int numCompletions)
{
// TODO: Implement this method
// This method should implement the following algorithm:
// 1. Find the stem in the trie. If the stem does not appear in the trie, return an
// empty list
// 2. Once the stem is found, perform a breadth first search to generate completions
// using the following algorithm:
// Create a queue (LinkedList) and add the node that completes the stem to the back
// of the list.
// Create a list of completions to return (initially empty)
// While the queue is not empty and you don't have enough completions:
// remove the first Node from the queue
// If it is a word, add it to the completions list
// Add all of its child nodes to the back of the queue
// Return the list of completions
List<String> completions=null;
int counter=0;
if (prefix==null){
return Collections.emptyList();
}
prefix=prefix.toLowerCase();
if(isWord(prefix))
completions.add(prefix);
LinkedList nodes = new LinkedList();
TrieNode curr=searchNode(prefix);
nodes.addLast(curr);
while(!nodes.isEmpty() && counter!=numCompletions)
{
if((nodes.removeFirst()).isWord)
completions.add(curr.getText());
TrieNode next = null;
for (Character c : curr.getValidNextCharacters()) {
next = curr.getChild(c);
}
}
return Collections.emptyList();
}
public void checkNull(String word){
if (word==null)
throw new NullPointerException("Null word passed");
}
// For debugging
public void printTree()
{
printNode(root);
}
/** Do a pre-order traversal from this node down */
public void printNode(TrieNode curr)
{
if (curr == null)
return;
System.out.println(curr.getText());
TrieNode next = null;
for (Character c : curr.getValidNextCharacters()) {
next = curr.getChild(c);
printNode(next);
}
}
}
And this is the code of the TrieNode class:
package spelling;
import java.util.HashMap;
import java.util.Set;
/**
* Represents a node in a Trie
* #author UC San Diego Intermediate Programming MOOC Team
*
*/
class TrieNode {
HashMap<Character, TrieNode> children;
private String text; // Maybe omit for space
boolean isWord;
/** Create a new TrieNode */
public TrieNode()
{
children = new HashMap<Character, TrieNode>();
text = "";
isWord = false;
}
/** Create a new TrieNode given a text String to store in it */
public TrieNode(String text)
{
this();
this.text = text;
}
/** Return the TrieNode that is the child when you follow the
* link from the given Character
* #param c The next character in the key
* #return The TrieNode that character links to, or null if that link
* is not in the trie.
*/
public TrieNode getChild(Character c)
{
return children.get(c);
}
/** Inserts this character at this node.
* Returns the newly created node, if c wasn't already
* in the trie. If it was, it does not modify the trie
* and returns null.
* #param c The character that will link to the new node
* #return The newly created TrieNode, or null if the node is already
* in the trie.
*/
public TrieNode insert(Character c)
{
if (children.containsKey(c)) {
return null;
}
TrieNode next = new TrieNode(text + c.toString());
children.put(c, next);
return next;
}
/** Return the text string at this node */
public String getText()
{
return text;
}
/** Set whether or not this node ends a word in the trie. */
public void setEndsWord(boolean b)
{
isWord = b;
}
/** Return whether or not this node ends a word in the trie. */
public boolean endsWord()
{
return isWord;
}
/** Return the set of characters that have links from this node */
public Set<Character> getValidNextCharacters()
{
return children.keySet();
}
}
Even though the algorithm is there I am not able to implement it. Any kind of help would be greatly appreciated.
are you trying to solve this as part of the Coursera's university of San Diego course?
If so then all what you have to do is to follow the algorithm that was written as a comment inside the class.
Any way, I added here a copy of my implementation to this method. Just don't copy and paste it as part of your solution please. Use it as guidance. I added comments in the code to help you understanding my algorithm:
//Trying to find the stem in Trie
String prefixToCheckLowerCase = prefix.toLowerCase();
int completionsCount = 0;
List<String> completions = new LinkedList<String>();
TrieNode traversal = root;
for (int i = 0; i < prefixToCheckLowerCase.length(); i++)
{
if (traversal.getValidNextCharacters().contains(prefixToCheckLowerCase.charAt(i)))
{
traversal = traversal.getChild(prefixToCheckLowerCase.charAt(i));
}
//Means stem not found, returns an empty list
else
return completions;
}
//If current word is an end word, increment the counter and add it to compeltions list
if (traversal.endsWord())
{
completionsCount=1;
completions.add(traversal.getText());
}
List<TrieNode> nodesToBeSearched = new LinkedList<TrieNode>();
List<Character> ChildCharaterList = new LinkedList<Character>(traversal.getValidNextCharacters());
//Filling the list with children of the current node, first level of of the breadth first search
for (int i=0; i<ChildCharaterList.size(); i++)
{
nodesToBeSearched.add(traversal.getChild(ChildCharaterList.get(i)));
}
//while loop for the linked list elements and see if any compeltions exists , inside it we will also check each node children and add them to the list!!!
while (nodesToBeSearched!=null && nodesToBeSearched.size()>0 && completionsCount < numCompletions)
{
TrieNode trieNode = nodesToBeSearched.remove(0);
if (trieNode.endsWord())
{
completionsCount++;
completions.add(trieNode.getText());
}
List<Character> subTrieNodeCholdren = new LinkedList<Character>(trieNode.getValidNextCharacters());
//Adding all next level tries to the linked list , kinda recursive!!!
for (int i=0; i<subTrieNodeCholdren.size();i++)
{
nodesToBeSearched.add(trieNode.getChild(subTrieNodeCholdren.get(i)));
}
}
return completions;
import java.util.ArrayList;
class TrieNode{
char data;
boolean isTerminating;
TrieNode children[];
int childCount;
public TrieNode(char data) {
this.data = data;
isTerminating = false;
children = new TrieNode[26];
childCount = 0;
}
}
public class Trie {
private TrieNode root;
//ArrayList<String> ans=new ArrayList<>();
public Trie() {
root = new TrieNode('\0');
}
private void add(TrieNode root, String word){
if(word.length() == 0){
root.isTerminating = true;
return;
}
int childIndex = word.charAt(0) - 'a';
TrieNode child = root.children[childIndex];
if(child == null){
child = new TrieNode(word.charAt(0));
root.children[childIndex] = child;
root.childCount++;
}
add(child, word.substring(1));
}
public void add(String word){
add(root, word);
}
private void searchHelper(TrieNode root,String word,String ans)
{
try
{
if(word.length()==0)
{
if(root.isTerminating == true)
{
System.out.println(ans);
}
for(int i=0;i<26;i++)
{
TrieNode temp=root.children[i];
if(temp !=null)
{
//ans=ans+temp.data;
//System.out.println("test check "+ans );
searchHelper(temp,word,ans+temp.data);
}
}
}
int childIndex=word.charAt(0)-'a';
TrieNode child=root.children[childIndex];
if(child == null)
{
//System.out.print();
return ;
}
ans=ans+word.charAt(0);
searchHelper(child,word.substring(1),ans);
}
catch(Exception e)
{
//System.out.println("error");
}
}
public void search(String word)
{
String s="";
searchHelper(root,word,s);
}
public void autoComplete(ArrayList<String> input, String word) {
// Complete this function
// Print the output as specified in question
Trie ansTrie = new Trie();
for(int i=0;i<input.size();i++)
{
ansTrie.add(input.get(i));
}
ansTrie.search(word);
}
}
i hope it helps in solving you doubt.
i am already sorry for any indentation errors .
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import java.util.stream.Collectors;
public class TrieImpl {
static class Element {
private Trie trie;
private String word;
Element(Trie trie, String word) {
this.trie = trie;
this.word = word;
}
}
static class Trie {
private boolean isLeaf;
private Map<Character, Trie> children;
private Map<Character, Integer> character;
Trie() {
isLeaf = false;
children = new HashMap<>();
character = new HashMap<>();
}
public void insert(String word) {
Trie curr = this;
for (Character ch : word.toCharArray()) {
curr.children.putIfAbsent(ch, new Trie());
int count = (curr.character.get(ch) == null) ? 1 : curr.character.get(ch) + 1;
curr.character.put(ch, count);
curr = curr.children.get(ch);
}
curr.isLeaf = true;
}
public boolean search(String word) {
Trie curr = this;
for (Character ch : word.toCharArray()) {
if (curr.children.get(ch) == null)
return false;
curr = curr.children.get(ch);
}
return curr.isLeaf;
}
public void delete(String word) {
if (search(word)) {
Trie lastSecond = this;
Character charToRemove = word.charAt(0);
Trie curr = this;
int i = -1;
while (i < word.length() && curr != null) {
if (curr.isLeaf && i != word.length() - 1) {
charToRemove = word.charAt(i + 1);
lastSecond = curr;
}
i = i + 1;
if (i < word.length())
curr = curr.children.get(word.charAt(i));
}
lastSecond.children.remove(charToRemove);
}
}
public int findPrefixCount(String word) {
Trie curr = this;
Character lastChar = null;
int count = 0;
for (Character ch : word.toCharArray()) {
if (curr.children.get(ch) == null)
return 0;
if (count < word.length() - 1) {
curr = curr.children.get(ch);
count++;
}
lastChar = ch;
}
if (lastChar != null && curr.character.get(lastChar) != null)
return curr.character.get(lastChar);
else
return 0;
}
public Set<String> autoComplete(String word) {
Trie curr = this;
int count = 0;
String wo = "";
Queue<Element> queue = new LinkedList<>();
Set<String> set = new HashSet<>();
for (Character ch : word.toCharArray()) {
if (count < word.length()) {
curr = curr.children.get(ch);
count++;
wo += ch;
}
}
if (curr != null)
queue.add(new Element(curr, wo));
while (!queue.isEmpty()) {
Element elem = queue.poll();
Trie current = elem.trie;
String temp = elem.word;
if (current != null && current.isLeaf)
set.add(temp);
List<Character> keys = current.character.keySet().stream().collect(Collectors.toList());
for (int i = 0; i < current.children.size(); i++) {
queue.add(new Element(current.children.get(keys.get(i)), temp + keys.get(i)));
}
}
return set;
}
}
public static void main(String[] args) {
Trie head = new Trie();
head.insert("techie");
head.insert("techi");
head.insert("tech");
head.insert("tecabc");
head.insert("tecabk");
head.insert("tecabd");
head.insert("tecalmz");
Set<String> words = head.autoComplete("t");
words.stream().forEach(x -> System.out.println(x));
}
}

Searching a 2D char array for occurrences

I'm hoping to find a bit of direction for this problem I was given. Been banging my head over it for two weeks now. Essentially, I'm to write a function, public static int FindWords(char[][] puzzle) , where given a 2D char array, I can find the amount of times a given set of strings occur. Given:
public static class PuzzleSolver
{
public static string[] DICTIONARY = {"OX","CAT","TOY","AT","DOG","CATAPULT","T"};
static bool IsWord(string testWord)
{
if (DICTIONARY.Contains(testWord))
return true;
return false;
}
}
A 2D Array for instance that is like this:
public static char[][] puzzle = {{'C','A','T'},
{'X','Z','T'},
{'Y','O','T'}};
Would return back 8 for the following instances: (AT, AT, CAT, OX, TOY, T, T, T) because we would be searching horizontally, vertically, diagonally and in reverse for all the same directions.
My approach was to visit each char in the array and then search for all possible directions with the SearchChar function...
public static int FindWords(char[][] puzzle){
int arrayRow = puzzle.length;
int arrayCol = puzzle[0].length;
int found = 0;
for(int i = 0; i < arrayRow; i++){
for(int j = 0; j < arrayCol; j++){
found += SearchChar(i,j);
}
}
return found;
}
Which looks like this:
public static int SearchChar(int row, int col){
if(row < 0 || col < 0 || row > puzzle.length || col > puzzle[0].length)//Is row or col out of bounds?
return 0;
if(IsWord(puzzle[row][col]))
return 1;
return 0;
}
Conceptually, I feel like I need some kind of recursive function to handle this but I can't seem to wrap my head around it. I don't even know if that's the right approach. I've been playing around with StringBuilder appending but I'm still struggling with that too. I think this recursive function would need to take for instance, puzzle[0][0] (or 'C') and see if it is in the dictionary. Followed by puzzle[0][0] + puzzle[0][1] (or 'CA') and then finally puzzle[0][0] + puzzle[0][1] + puzzle[0][2] (or 'CAT'). Then the same would have to be don vertically and diagonally. I'm having trouble trying to get back into the SearchChar function with a position change to append to the original char so that I can see if it is in the DICTIONARY.
Sorry if this is a bit wordy, but I just want to give the impression that I'm actually trying to solve this. Not just some lazy programmer that's copy & pasting some problem up here for someone else to solve. Thanks in advance for any help!
I will show you how to solve this problem step by step.
1. Generating All Possible Words from the given Puzzle
to do this we must start anywhere in the puzzle and move towards all directions (except the previous Point) to generate all possible words;
2. Choosing Suitable Data Structure for Dictionary
I think Trie is a good choice and is suitable for use in such situations.
The most important reason for choosing Trie is that during the search, we can easily test if a word exists in our dictionary or is there any word that starts with the word generated by searching through the puzzle or not.
As a result, we can decide whether or not to continue the search.
This will save us a lot of time and helps to generate words correctly.
otherwise, we'll be stuck in an endless loop...
3. Implementation
there are several implementations for Tire , but I wrote my own CharTrie :
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
/**
* #author FaNaJ
*/
public final class CharTrie {
/**
* Pointer to root Node
*/
private final Node root = new Node();
/**
* Puts the specified word in this CharTrie and increases its frequency.
*
* #param word word to put in this CharTrie
* #return the previous frequency of the specified word
*/
public int put(String word) {
if (word.isEmpty()) {
return 0;
}
Node current = root;
for (int i = 0; i < word.length(); i++) {
current = current.getChildren().computeIfAbsent(word.charAt(i), ch -> new Node());
}
return current.getAndIncreaseFrequency();
}
/**
* #param word the word whose frequency is to be returned
* #return the current frequency of the specified word or -1 if there isn't such a word in this CharTrie
*/
public int frequency(String word) {
if (word.isEmpty()) {
return 0;
}
Node current = root;
for (int i = 0; i < word.length() && current != null; i++) {
current = current.getChildren().get(word.charAt(i));
}
return current == null ? -1 : current.frequency;
}
/**
* #param word the word whose presence in this CharTrie is to be tested
* #return true if this CharTrie contains the specified word
*/
public boolean contains(String word) {
return frequency(word) > 0;
}
/**
* #return a CharTrie Iterator over the Nodes in this CharTrie, starting at the root Node.
*/
public Iterator iterator() {
return new Iterator(root);
}
/**
* Node in the CharTrie.
* frequency-children entry
*/
private static final class Node {
/**
* the number of occurrences of the character that is associated to this Node,
* at certain position in the CharTrie
*/
private volatile int frequency = 0;
private static final AtomicIntegerFieldUpdater<Node> frequencyUpdater
= AtomicIntegerFieldUpdater.newUpdater(Node.class, "frequency");
/**
* the children of this Node
*/
private Map<Character, Node> children;
public Map<Character, Node> getChildren() {
if (children == null) {
children = new ConcurrentHashMap<>();
}
return children;
}
/**
* Atomically increments by one the current value of the frequency.
*
* #return the previous frequency
*/
private int getAndIncreaseFrequency() {
return frequencyUpdater.getAndIncrement(this);
}
}
/**
* Iterator over the Nodes in the CharTrie
*/
public static final class Iterator implements Cloneable {
/**
* Pointer to current Node
*/
private Node current;
private Iterator(Node current) {
this.current = current;
}
/**
* Returns true if the current Node contains the specified character in its children,
* then moves to the child Node.
* Otherwise, the current Node will not change.
*
* #param ch the character whose presence in the current Node's children is to be tested
* #return true if the current Node's children contains the specified character
*/
public boolean next(char ch) {
Node next = current.getChildren().get(ch);
if (next == null) {
return false;
}
current = next;
return true;
}
/**
* #return the current frequency of the current Node
*/
public int frequency() {
return current.frequency;
}
/**
* #return the newly created CharTrie Iterator, starting at the current Node of this Iterator
*/
#Override
#SuppressWarnings("CloneDoesntCallSuperClone")
public Iterator clone() {
return new Iterator(current);
}
}
}
and the WordGenerator :
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveAction;
import java.util.function.BiConsumer;
/**
* #author FaNaJ
*/
public final class WordGenerator {
private WordGenerator() {
}
public static void generate(char[][] table, CharTrie.Iterator iterator, BiConsumer<String, Integer> action) {
final ForkJoinPool pool = ForkJoinPool.commonPool();
final VisitorContext ctx = new VisitorContext(table, action);
for (int y = 0; y < table.length; y++) {
for (int x = 0; x < table[y].length; x++) {
pool.invoke(new Visitor(new Point(x, y), null, "", iterator.clone(), ctx));
}
}
}
private static final class VisitorContext {
private final char[][] table;
private final BiConsumer<String, Integer> action;
private VisitorContext(char[][] table, BiConsumer<String, Integer> action) {
this.table = table;
this.action = action;
}
private boolean validate(Point point) {
Object c = null;
try {
c = table[point.getY()][point.getX()];
} catch (ArrayIndexOutOfBoundsException ignored) {
}
return c != null;
}
}
private static final class Visitor extends RecursiveAction {
private final Point current;
private final Point previous;
private final CharTrie.Iterator iterator;
private final VisitorContext ctx;
private String word;
private Visitor(Point current, Point previous, String word, CharTrie.Iterator iterator, VisitorContext ctx) {
this.current = current;
this.previous = previous;
this.word = word;
this.iterator = iterator;
this.ctx = ctx;
}
#Override
protected void compute() {
char nextChar = ctx.table[current.getY()][current.getX()];
if (iterator.next(nextChar)) {
word += nextChar;
int frequency = iterator.frequency();
if (frequency > 0) {
ctx.action.accept(word, frequency);
}
List<Visitor> tasks = new ArrayList<>();
for (Direction direction : Direction.values()) {
Point nextPoint = direction.move(current);
if (!nextPoint.equals(previous) && ctx.validate(nextPoint)) {
tasks.add(new Visitor(nextPoint, current, word, iterator.clone(), ctx));
}
}
invokeAll(tasks);
}
}
}
}
Note that I've used ForkJoinPool and RecursiveAction to speed up the search.
learn more :
https://docs.oracle.com/javase/tutorial/essential/concurrency/forkjoin.html
http://tutorials.jenkov.com/java-util-concurrent/java-fork-and-join-forkjoinpool.html
http://www.javaworld.com/article/2078440/enterprise-java/java-tip-when-to-use-forkjoinpool-vs-executorservice.html
the rest of classes :
PuzzleSolver
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiConsumer;
import java.util.stream.Stream;
/**
* #author FaNaJ
*/
public final class PuzzleSolver {
private final CharTrie dictionary;
public enum OrderBy {FREQUENCY_IN_DICTIONARY, FREQUENCY_IN_PUZZLE}
public PuzzleSolver(CharTrie dictionary) {
this.dictionary = dictionary;
}
public CharTrie getDictionary() {
return dictionary;
}
public Stream<Word> solve(char[][] puzzle) {
return solve(puzzle, OrderBy.FREQUENCY_IN_DICTIONARY);
}
public Stream<Word> solve(char[][] puzzle, OrderBy orderBy) {
Stream<Word> stream = null;
switch (orderBy) {
case FREQUENCY_IN_DICTIONARY: {
final Map<String, Integer> words = new ConcurrentHashMap<>();
WordGenerator.generate(puzzle, dictionary.iterator(), words::put);
stream = words.entrySet().stream()
.map(e -> new Word(e.getKey(), e.getValue()));
break;
}
case FREQUENCY_IN_PUZZLE: {
final Map<String, AtomicInteger> words = new ConcurrentHashMap<>();
BiConsumer<String, Integer> action = (word, frequency) -> words.computeIfAbsent(word, s -> new AtomicInteger()).getAndIncrement();
WordGenerator.generate(puzzle, dictionary.iterator(), action);
stream = words.entrySet().stream()
.map(e -> new Word(e.getKey(), e.getValue().get()));
break;
}
}
return stream.sorted((a, b) -> b.compareTo(a));
}
}
http://winterbe.com/posts/2014/07/31/java8-stream-tutorial-examples/
Point
import java.util.Objects;
/**
* #author FaNaJ
*/
public final class Point {
private final int x;
private final int y;
public Point() {
this(0, 0);
}
public Point(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
#Override
public int hashCode() {
return x * 31 + y;
}
#Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
Point that = (Point) obj;
return x == that.x && y == that.y;
}
#Override
public String toString() {
return "[" + x + ", " + y + ']';
}
}
Word
/**
* #author FaNaJ
*/
public final class Word implements Comparable<Word> {
private final String value;
private final int frequency;
public Word(String value, int frequency) {
this.value = value;
this.frequency = frequency;
}
public String getValue() {
return value;
}
public int getFrequency() {
return frequency;
}
#Override
public int hashCode() {
return value.hashCode() * 31 + frequency;
}
#Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Word that = (Word) o;
return frequency == that.frequency && value.equals(that.value);
}
#Override
public String toString() {
return "{" +
"value='" + value + '\'' +
", frequency=" + frequency +
'}';
}
#Override
public int compareTo(Word o) {
return Integer.compare(frequency, o.frequency);
}
}
Direction
/**
* #author FaNaJ
*/
public enum Direction {
UP(0, 1), UP_RIGHT(1, 1), UP_LEFT(-1, 1),
RIGHT(1, 0), LEFT(-1, 0),
DOWN(0, -1), DOWN_RIGHT(1, -1), DOWN_LEFT(-1, -1);
private final int x, y;
Direction(int x, int y) {
this.x = x;
this.y = y;
}
public Point move(Point point) {
return new Point(point.getX() + x, point.getY() + y);
}
}
4. Test it
/**
* #author FaNaJ
*/
public class Test {
public static String[] DICTIONARY = {"OX", "CAT", "TOY", "AT", "DOG", "CATAPULT", "T", "AZOYZACZOTACXY"};
public static void main(String[] args) {
CharTrie trie = new CharTrie();
for (String word : DICTIONARY) {
trie.put(word);
}
PuzzleSolver solver = new PuzzleSolver(trie);
char[][] puzzle = {
{'C', 'A', 'T'},
{'X', 'Z', 'T'},
{'Y', 'O', 'T'}
};
solver.solve(puzzle, PuzzleSolver.OrderBy.FREQUENCY_IN_PUZZLE).forEach(System.out::println);
}
}
output :
{value='T', frequency=3}
{value='AT', frequency=2}
{value='CAT', frequency=2}
{value='TOY', frequency=2}
{value='OX', frequency=1}
{value='AZOYZACZOTACXY', frequency=1}

Initialize an Item Array

I want to initialize an Item Array but can't figure it out.
Here is my code.
public class HashTable<Item> {
private int m; // hash table size
private Item[] T; // hash table
HashTable(int M)
{
m = M;
T = new Item[M];
for(int i=0;i<M;i++){
Item T[i] = null;
}
}
...
...
SOLUTION
T = (Item[])new Object[M];
I think what you need is something like:
for(int i=0;i<M;i++){
T[i] = new Item(); // call some constructor here
}
You have
Item T[i] = ...
in your loop, while it should be just
T[i] = ...
So try these hints.
Also do this:
T = (Item[])new Object[M];
as Kumar suggested in his reply.
The thing is that Item is not really a type here. You need
to read how generics are actually compiled into bytecode,
and you will see what happens under the hood.
You are trying to create the array of Generic type,Please look at this post.
How to create a generic array in Java?
Assuming you created the class item you could call it like this:
public class HashTable
{
private int tableSize; // hash table size
private Item[] table; // hash table
public static void main(String[] args)
{
// where 10 is the number of nodes
Item[] myTable = createHashTable(10);
}
private static Item[] createHashTable(int size)
{
Item[] table = new Item[size];
for(int i = 0; i < table.length; i++)
{
table[i] = new Item(i);
}
}
}
However if you want to see an example of a full HashTable implementation:
/*
* HashTable.java
*
*
*/
/**
* A class that implements a hash table that employs open addressing
* using either linear probing, quadratic probing, or double hashing.
*/
public class HashTable {
/* Private inner class for an entry in the hash table */
private class Entry {
private String key;
private LLList valueList; // all of the values with this key
private boolean hasBeenRemoved; // has this entry been removed?
private Entry(String key, int value) {
this.key = key;
valueList = new LLList();
valueList.addItem(value, 0);
hasBeenRemoved = false;
}
}
// parameters for the second hash function -- see h2() below
private static final int H2_MIN = 5;
private static final int H2_DIVISOR = 11;
// possible types of probing
public static final int LINEAR = 0;
public static final int QUADRATIC = 1;
public static final int DOUBLE_HASHING = 2;
public static final int NUM_PROBE_TYPES = 3;
private Entry[] table; // the hash table itself
private int probeType = LINEAR; // the type of probing
// keeps track of how many times we perform a probe of a given length
private int[] probeLengthCount;
public HashTable(int size, int probeType) {
if (probeType >= 0 && probeType < NUM_PROBE_TYPES)
this.probeType = probeType;
else
throw new IllegalArgumentException("invalid probeType: " +
probeType);
table = new Entry[size];
probeLengthCount = new int[size + 1];
for (int i = 0; i <= size; i++)
probeLengthCount[i] = 0;
}
public HashTable(int size) {
// Call the other constructor to do the work.
this(size, LINEAR);
}
/* first hash function */
private int h1(String key) {
int h1 = key.hashCode() % table.length;
if (h1 < 0)
h1 += table.length;
return h1;
}
/* second hash function */
private int h2(String key) {
int h2 = key.hashCode() % H2_DIVISOR;
if (h2 < 0)
h2 += H2_DIVISOR;
h2 += H2_MIN;
return h2;
}
/*
* probeIncrement - returns the amount by which the current index
* should be incremented to obtain the nth element in the probe
* sequence
*/
private int probeIncrement(int n, int h2) {
if (n <= 0)
return 0;
switch (probeType) {
case LINEAR:
return 1;
case QUADRATIC:
return (2*n - 1);
case DOUBLE_HASHING:
default:
return h2;
}
}
/*
* probe - attempt to find a slot in the hash table for the specified key.
*
* If key is currently in the table, it returns the index of the entry.
* If key isn't in the table, it returns the index of the first empty cell
* in the table.
* If overflow occurs, it returns -1.
*/
private int probe(String key) {
int i = h1(key); // first hash function
int h2 = h2(key); // second hash function
int positionsChecked = 1;
// keep probing until we get an empty position or a match
while (table[i] != null && !key.equals(table[i].key)) {
if (positionsChecked == table.length) {
probeLengthCount[positionsChecked]++;
return -1;
}
i = (i + probeIncrement(positionsChecked, h2)) % table.length;
positionsChecked++;
}
probeLengthCount[positionsChecked]++;
return i;
}
/**
* insert - insert the specified (key, value) pair in the hash table
*/
public void insert(String key, int value) {
if (key == null)
throw new IllegalArgumentException("key must be non-null");
int i = h1(key);
int h2 = h2(key);
int positionsChecked = 1;
int firstRemoved = -1;
while (table[i] != null && !key.equals(table[i].key)) {
if (table[i].hasBeenRemoved && firstRemoved == -1)
firstRemoved = i;
if (positionsChecked == table.length)
break;
i = (i + probeIncrement(positionsChecked, h2)) % table.length;
positionsChecked++;
}
probeLengthCount[positionsChecked]++;
if (table[i] != null && key.equals(table[i].key))
table[i].valueList.addItem(value, 0);
else if (firstRemoved != -1)
table[firstRemoved] = new Entry(key, value);
else if (table[i] == null)
table[i] = new Entry(key, value);
else
throw new RuntimeException("overflow occurred");
}
/**
* search - search for the specified key, and return the
* associated list of values, or null if the key is not in the
* table
*/
public LLList search(String key) {
if (key == null)
throw new IllegalArgumentException("key must be non-null");
int i = probe(key);
if (i == -1 || table[i] == null)
return null;
else
return table[i].valueList;
}
/**
* remove - remove from the table the entry for the specified key
*/
public void remove(String key) {
if (key == null)
throw new IllegalArgumentException("key must be non-null");
int i = probe(key);
if (i == -1 || table[i] == null)
return;
table[i].key = null;
table[i].valueList = null;
table[i].hasBeenRemoved = true;
}
/**
* printStats - print the statistics for the table -- i.e., the
* number of keys and items, and stats for the number of times
* that probes of different lengths were performed
*/
public void printStats() {
int numProbes = 0;
int probeLengthSum = 0;
int numKeys = 0;
for (int i = 0; i < table.length; i++) {
if (table[i] != null && !table[i].hasBeenRemoved)
numKeys++;
}
System.out.println("\n" + numKeys + " keys");
System.out.println("probe-length stats:");
System.out.println("length\tcount");
for (int i = 1; i <= table.length; i++) {
if (probeLengthCount[i] != 0)
System.out.println(i + "\t" + probeLengthCount[i]);
numProbes += probeLengthCount[i];
probeLengthSum += (probeLengthCount[i] * i);
}
System.out.println("average probe length = " +
(double)probeLengthSum / numProbes);
}
}
Here is the second file for a Linked-Linked-List
/*
* LLList.java
*
*
*/
import java.util.*;
/**
* A class that implements our simple List interface using a linked list.
* The linked list includes a dummy head node that allows us to avoid
* special cases for insertion and deletion at the front of the list.
*/
public class LLList implements List {
// Inner class for a node. We use an inner class so that the LLList
// methods can access the instance variables of the nodes.
private class Node {
private Object item;
private Node next;
private Node(Object i, Node n) {
item = i;
next = n;
}
}
private Node head; // dummy head node
private int length; // # of items in the list
/**
* Constructs a LLList object for a list that is initially empty.
*/
public LLList() {
head = new Node(null, null);
length = 0;
}
/*
* getNode - private helper method that returns a reference to the
* ith node in the linked list. It assumes that the value of the
* parameter is valid.
*
* If i == -1, it returns a reference to the dummy head node.
*/
private Node getNode(int i) {
Node trav = head;
int travIndex = -1;
while (travIndex < i) {
travIndex++;
trav = trav.next;
}
return trav;
}
/** getItem - returns the item at position i in the list */
public Object getItem(int i) {
if (i < 0 || i >= length)
throw new IndexOutOfBoundsException();
Node n = getNode(i);
return n.item;
}
/**
* addItem - adds the specified item at position i in the list,
* shifting the items that are currently in positions i, i+1, i+2,
* etc. to the right by one. Always returns true, because the list
* is never full.
*
* We don't need a special case for insertion at the front of the
* list (i == 0), because getNode(0 - 1) will return the dummy
* head node, and the rest of insertion can proceed as usual.
*/
public boolean addItem(Object item, int i) {
if (i < 0 || i > length)
throw new IndexOutOfBoundsException();
Node newNode = new Node(item, null);
Node prevNode = getNode(i - 1);
newNode.next = prevNode.next;
prevNode.next = newNode;
length++;
return true;
}
/**
* removeItem - removes the item at position i in the list,
* shifting the items that are currently in positions i+1, i+2,
* etc. to the left by one. Returns a reference to the removed
* object.
*
* Here again, we don't need a special case for i == 0 (see the
* note accompanying addItem above).
*/
public Object removeItem(int i) {
if (i < 0 || i >= length)
throw new IndexOutOfBoundsException();
Node prevNode = getNode(i - 1);
Object removed = prevNode.next.item;
prevNode.next = prevNode.next.next;
length--;
return removed;
}
/** length - returns the number of items in the list */
public int length() {
return length;
}
/**
* isFull - always returns false, because the linked list can
* grow indefinitely and thus the list is never full.
*/
public boolean isFull() {
return false;
}
/**
* toString - converts the list into a String of the form
* [ item0 item1 ... ]
*/
public String toString() {
String str = "[ ";
Node trav = head.next; // skip over the dummy head node
while (trav != null) {
str += (trav.item + " ");
trav = trav.next;
}
str += "]";
return str;
}
/**
* iterator - returns an iterator for this list
*/
public ListIterator iterator() {
return new LLListIterator();
}
/*
*** private inner class for an iterator over an LLList ***
*/
private class LLListIterator implements ListIterator {
private Node nextNode; // the next node to visit
private Node lastVisitedNode; // the most recently visited node
public LLListIterator() {
nextNode = head.next;
lastVisitedNode = null;
}
/**
* hasNext - does the iterator have additional items to visit?
*/
public boolean hasNext() {
return (nextNode != null);
}
/**
* next - returns a reference to the next Object in the iteration
*/
public Object next() {
if (nextNode == null)
throw new NoSuchElementException();
Object item = nextNode.item;
lastVisitedNode = nextNode;
nextNode = nextNode.next;
return item;
}
}
}

Tree Print Tree Method

I am going over my old test from Data Structures, and I cannot figure out how to implement the printtree(int level) method in my Tree class. I am restricted to using this structure. I cannot figure out an implementation to use without root.right or root.left, this is very frustrating.
/*
Exam 2. Problem 2. 03/09/2012
*/
import java.util.List;
import java.util.ArrayList;
/**
A tree in which each node has an arbitrary number of children.
*/
public class Tree
{
private Node root;
class Node
{
public Object data;
public List<Node> children;
/**
Computes the size of the subtree whose root is this node.
#return the number of nodes in the subtree
*/
public int size()
{
int sum = 0;
for (Node child : children) { sum = sum + child.size(); }
return 1 + sum;
}
public int leaves() {
int count = 0;
for (Node child : children) {
if (child.size() == 1) {
count = count + 1;
} else {
count = count + child.leaves();
}
}
if (count == 0) {
count = count + 1;
}
return count;
}
public String printTree(int level) {
String S = "";
S += root.data + " (level:" + level + ") ";
if (root != null) {
return S;
}
if (root.children != null) {
S += root.printTree(level + 1);
}
return S;
}
}
/**
Constructs an empty tree.
*/
public Tree()
{
root = null;
}
/**
Constructs a tree with one node and no children.
#param rootData the data for the root
*/
public Tree(Object rootData)
{
root = new Node();
root.data = rootData;
root.children = new ArrayList<Node>();
}
/**
Adds a subtree as the last child of the root.
*/
public void addSubtree(Tree subtree)
{
root.children.add(subtree.root);
}
/**
Computes the size of this tree.
#return the number of nodes in the tree
*/
public int size()
{
if (root == null) { return 0; }
else { return root.size(); }
}
public int leaves() {
Node newNode = root;
if (root == null) { return 0; }
else { return root.leaves(); }
}
public String printTree() {
return root.children.printTree(0);
}
}
you just need to change 3 things:
Change root to this every where in the printTree(int level) method
The placement of if(this == null) should be checked before anything else
Use a for loop to print all of the children
public String printTree(int level) {
String S = "";
// notice the change to '=='
if (this == null)
return S;
S += this.data + " (level:" + level + ") ";
// notice the for loop
if( this.children != null)
for(Node child : this.children)
S += child.printTree(level + 1);
return S;
}

Categories