So here is the Node class:
public class Node
{
private int _info;
private Node _left;
private Node _right;
public Node()
{
//this._info = Integer.MIN_VALUE;
this._left = null;
this._right = null;
}
public int getInfo()
{
return _info;
}
public void setInfo(int _info)
{
this._info = _info;
}
public Node getLeft()
{
return _left;
}
public void setLeft(Node _left)
{
this._left = _left;
}
public Node getRight()
{
return _right;
}
public void setRight(Node _right)
{
this._right = _right;
}
}
How I create the tree:
public class BalancedBinaryTree
{
private ArrayList<Integer> _numbers;
private Node _root;
public BalancedBinaryTree(ArrayList<Integer> numbers)
{
this._numbers = new ArrayList<>();
this._numbers.addAll(numbers);
Collections.sort(this._numbers);
this._root = new Node();
this.create(this._root, 0, this._numbers.size());
}
private void create(Node tree, int i, int j)
{
if (i < j)
{
int m = i + (j - i) / 2;
tree.setInfo(this._numbers.get(m));
tree.setLeft(new Node());
create(tree.getLeft(), i, m);
tree.setRight(new Node());
create(tree.getRight(), m + 1, j);
}
}
This method computes the depth:
public static int getDepth(Node node)
{
if (node == null)
{
return 0;
}
else
{
int max = 0;
if (getDepth(node.getLeft()) > getDepth(node.getRight()))
{
max = getDepth(node.getLeft());
}
else
{
max = getDepth(node.getRight());
}
return max + 1;
}
}
And these two combined should print the tree by its levels:
public static void printLevel(Node node, int levelToDisplay, int currentLevel)
{
if (node != null)
{
printLevel(node.getLeft(), levelToDisplay, currentLevel);
if (currentLevel == levelToDisplay)
{
System.out.print(node.getInfo() + " ");
}
currentLevel++;
printLevel(node.getRight(), levelToDisplay, currentLevel);
}
}
public static void printLevels(Node node)
{
for (int i = 0; i < getDepth(node); i++)
{
System.out.println("Level :" + i);
printLevel(node, i, 0);
System.out.println();
}
}
In a test class I have:
testNumbers.add(15);
testNumbers.add(20);
testNumbers.add(25);
testNumbers.add(30);
testNumbers.add(35);
testNumbers.add(40);
testNumbers.add(45);
BalancedBinaryTree tree = new BalancedBinaryTree(testNumbers);
BalancedBinaryTree.printLevels(tree.getRoot());
And I get this output:
Level :0
0 15 20 30
Level :1
0 0 25 0 35 40
Level :2
0 0 0 45
Level :3
0
I should get
Level :0
30
Level :1
20 40
Level :2
15 25 35 45
What's wrong with the getDepth method because it seems that it returns 4 levels instead of 3?
Why are there additional nodes? (those zeroes)
I'm pretty sure I solved the problems but I will need an explanation for the following:
This is the modified printlevel method:
public static void printLevel(Node node, int levelToDisplay, int currentLevel)
{
if (node.getLeft() != null && node.getRight() != null)
{
printLevel(node.getLeft(), levelToDisplay, currentLevel+1);
if (currentLevel == levelToDisplay)
{
System.out.print(node.getInfo() + " ");
}
printLevel(node.getRight(), levelToDisplay, currentLevel+1);
}
}
As you can see I test now if the current node has childs instead of checking if the current node exists and this is why those zeroes appeard because the traversal reached the leafs that had no info assigned on their right and left childs.
The thing I want to understand is the difference between incrementing currentLevel and then passing it to the call of printLevel and simply passing currentLevel+1 to the call. Shouldn't it be the same thing ?
And the getDepth function:
public static int getDepth(Node node)
{
if (node.getLeft() == null && node.getRight() == null)
{
return 0;
}
else
{
int max = 0;
if (getDepth(node.getLeft()) > getDepth(node.getRight()))
{
max = getDepth(node.getLeft());
}
else
{
max = getDepth(node.getRight());
}
return 1 + max;
}
}
Same thing here: traversal reached the leafs and got one more call for its childs thus returning one additional level so again, the solution is to test if the current node has childs instead of checking if the current node exits.
What's wrong with the getDepth method because it seems that it returns 4 levels instead of 3?
From your print method it seems, that you number the levels from 0 to n (the root of a tree beeing 0). Your getDepth method however will never return 0.
Two things: if (node != null) this check does not seem to make very much sense. Null does not seem to be an allowed input (as the root is constructed on construction of a Tree). If this is the case (and you do want to check it) an exception might be more appropriate.
The main problem seems to be this: return max + 1;
So the minimal value returned is 0 + 1, which is 1.
As a small sidenote: I would save the values of the two recursive calls of getDepth, it would greatly increase performance.
Also, if you do use short variable names such as i, m or j (in a non-loop index kind of way) it would be helpful to document their meaning.
And conserning your first question:
tree.setLeft(new Node());
What would be the value of this Node as of now? And what will happen if the i < j codition in the recurive call will not pass? If you can answer those questions, you should be able to fix the code yourself.
Related
I am creating a program that inserts a character (number/letter) into a binary tree. So far, I'm able to produce an output but it's not what I expected. These are the problems I'm encountering:
The insert method is not able to print the correct height of the tree. I am not sure where I should insert my height++; statement to get the correct output.
The insert method is only able to add nodes to the right.
Expected Output: ht=3 [K=3 L=[K=1 R=[K=2]] R=[K=5 L=[K=4]]]
My Output: ht=4 [K=3 R=[K=1 R=[K=2 R=[K=5 R=[K=4]]]]
(all nodes are only added to the right 'R')
Here are my classes for reference:
Main Class
BST<Character> bst = new BST<>();
bst.insert('3');
bst.insert('1');
bst.insert('2');
bst.insert('5');
bst.insert('4');
System.out.println("ht=" + bst.height + " " + bst.toString());
BST Class - where the insert method is declared
public class BST<T> extends BT<T> {
// insert() method
public void insert(char k)
{
if (root == null) {
root = new BTNode(k);
return;
}
BTNode<T> n = root;
BTNode<T> p = null; // parent
while (n != null) {
p = n;
if (k < n.value) {
n = n.left;
} else {
n = n.right;
}
}
if (k < p.value) {
p.left = new BTNode(k);
} else {
p.right = new BTNode(k);
height++; // adds 1 to height when a new level is made
}
}
}
BTNode Class
public class BTNode<T> {
T info;
int value, level;
BTNode<T> left, right;
public BTNode(T el) {
this(el, null, null);
}
public BTNode(T el, BTNode<T> l, BTNode<T> r) {
info = el;
left = l;
right = r;
}
}
BT Class - where the toString method is declared
public class BT<T> {
BTNode<T> root = null;
int height = 0;
public BT() {
BTNode<T> node = new BTNode("");
}
// other methods
// toString()
public String toString() {
return toString(root);
}
public String toString(BTNode<T> n) {
String s = "";
if (n == null) {
return "";
}
if (n != null) {
s = "[K=" + n.info;
if (n.left != null) {
s = s + " L=" + toString(n.left) + "]";
}
if (n.right != null) {
s = s + " R=" + toString(n.right) + "]";
}
}
return s;
}
}
Hope you can help me out, thanks!
You have quite a few issues in your code. I'll list a few immediate items but you really will need to learn to use an interactive debugger and unit testing to resolve the sorts of issues you are seeing.
You refer to the value field in BTNode in your comparison but it is never set. You should really be referring to info (which is the actual data in the node).
But given info is a generic type you can't use standard comparison operators. Instead you'll need to define it as <T extends Comparable<T>> and then use n.info.compareTo(k) > 0.
The key passed into insert should also be of type T
Which means the other classes also need to ensure T extends Comparable.
Height is only incremented when nodes are added to the right which makes no sense.
Height needs to be increased only when a node is inserted further from the root than the current maximum. Something like the following:
int depth = 0;
while (n != null) {
depth++;
p = n;
...
}
depth++;
if (depth > height)
height = depth;
You should get used to making your fields private and accessing them through getters. In your case a compareValue method would likely make sense.
A similar question was asked here on SO: [BST with duplicates
User Sazzadur Rahaman posted the three scenarios for accomplishing BST with duplicates, but I need to know how to implement the third situation he mentioned which looks something like this:
Assume we are using the input: "RABSAB."
The tree with the counter variable in brackets would look like this:
R(1)
/ \
/ \
A(2) S(1)
\
\
B(2)
So basically, I want each element(node) to have a specific counter variable.
Is what I'm trying to do possible to implement in just my insert method? Or would I need some sort of other method in my BSTTree/Node class?
****EDIT** my BSTNode class, made changes from Compass's recommendations.
public class BSTNode {
String key;
BSTNode left, right;
int count=1;
public BSTNode(String key) {
this.key = key;
}
public void insert(String x) {
if (x.compareTo(this.key) > 0) {
if (right != null) {
right.insert(x);
} else {
right = new BSTNode(x);
}
} //Go right
else if (x.compareTo(this.key) < 0) {
if (left != null) {
left.insert(x);
} else {
left = new BSTNode(x);
}
} //Go left
else if (x.compareTo(this.key) == 0) {
count++;
} // It's a duplicate, update the count
}
}
EDIT, updated my output incrementing a counter like that doesn't seem to give the correct output, I'm inserting "RABSAB" and trying to count the number of duplicate nodes.
Inserting as follows:
String line = "R A B S A B";
String[] words = line.split(" ");
for (int i = 0; i < words.length; i++) {
t1 = t1.Insert(words[i], t1);
System.out.println(words[i] + " : " + t1.count);
}
I get the following output:
R : 1
A : 1
B : 1
S : 1
A : 1
B : 1
Thanks for your time everyone.
Inside your BSTNode class, outside of the Insert method, declare int counter = 1;
Then, within your else, you would do counter++;
So on creating a node, you'd have 1 of the element (since you created it, you know it exists).
As additional keys that match are found, you would increment the counter.
I do feel like there is an implementation issue with your Insert method (which should be lower-case insert to follow conventions). You're passing a Node t which I assume is the child of the Node. That should probably be declared as a field just like counter is.
Sample Pseudocoded Integer-based Node with Duplicates
public class Node() {
int value; //node's value
int counter = 1; //how many items of same type at node
Node leftChild = null;
Node rightChild = null;
public Node(int value) {
this.value = value;
}
public void insert(int newValue) {
if(newValue > value) {
if(rightChild != null)
rightChild.insert(newValue); //we tell the child to deal with it
else
rightChild = new Node(newValue); //we make it a child
}
else if(newValue < value) {
if(leftChild != null)
leftChild.insert(newValue);
else
leftChild = new Node(newValue);
}
else if(newValue == value) {
counter++; // we found a duplicate, increase count
}
}
}
This will work.
else {
t.count++;
}
return t;
We were given the Node class and we have to implement the recursive insertion. After the insertion my output is wrong and I don't know why
The function printTree was given:
public void printTree() {
printTree(root,0);
}
public void printTree(TreeNode<T> p, int depth) {
if (p != null) {
if (!(p.left == null && p.right == null))
printTree(p.right,depth+1);
for (int i = 1; i <= depth; i++)
System.out.print(" ");
if (p.left == null && p.right == null)
System.out.println(p.info + " ");
else System.out.println(p.info);
printTree(p.left,depth+1);
}
}
This is my insertion function:
public void insert(T info)
{
root = insert(root, info);
}
protected TreeNode<T> insert(TreeNode<T> node, T info)
{
if (node == null)
{
node = new TreeNode<T>(info);
}
else if (info.compareTo(node.info) < 0)
{
node.left = insert(node.left,info);
}
else
{
node.right = insert(node.right,info);
}
return node;
}
This is my main:
public static void main(String[] args) {
// TODO code application logic here
Tree<Integer> t = new Tree<Integer>();
t.insert(4);
t.insert(3);
t.insert(5);
t.insert(1);
t.printTree();
}
and the output I get is:
5
4
3
1
instead of:
4
3 5
1
The output seems ok to me.
The printTree method does not work as you expect. It prints the tree from top to bottom level, but on the screen the output is "rotated" by 90 deg
So the top level is meant to be on the first column, bottom level at the last non-empty column.
I'm struggling to understand why this class is not functioning. It was part of an assignment for a course on Data Structures(EDIT: The deadline for the assignment has passed, I just want to figure it out...). The node is part of an AVL tree built upon a BST and the way I chose to implement it is by creating methods within my Node class to find the Balance factor and height.
The class is structured as follows:
public class Node<T extends Comparable<? super T>> {
public T data;
public Node left;
public Node right;
public Node(T IN) {
data = IN;
}
public Node(T IN, Node L, Node R) {
this(IN);
left = L;
right = R;
}
#Override
public String toString() {
return data.toString();
}
#Override
public Node clone() {
return new Node(this.data) ;
}
public int getHeight() {
return getHeight(this) ;
}
public int getBF() {
//Calculate BF
int balanceFactor = 0;
if (right != null && left != null)
balanceFactor = getHeight(right) - getHeight(left);
else if (left != null) {
balanceFactor = 0 - getHeight(left) ;
}
else if (right != null) {
balanceFactor = getHeight(right) ;
}
else
balanceFactor = 0 ;
return balanceFactor ;
}
private int getHeight(Node p) {
if (p.left == null && p.right == null ) {
return 0 ;
}
else if (p.left != null && p.right != null) {
return 1 + max(p.left.getHeight(), p.right.getHeight());
}
else if (p.left != null) {
return 1 + p.left.getHeight() ;
}
else if (p.right != null) {
return 1 + p.right.getHeight() ;
}
else {
return 0;
}
}
private int max(int x, int y) {
if (x >= y) {
return x;
} else {
return y;
}
}
}
and the function calling the method is:
#Override
public boolean insert(T el) {
boolean test = super.insert(el) ;
if (test) {
return checkBalance(root) ;
}
else
return false ;
}
and the exception I recieve is a repetition of:
Exception in thread "main" java.lang.StackOverflowError
at Node.getHeight(Node.java:54)
at Node.getHeight(Node.java:33)
at Node.getHeight(Node.java:58)
I would suggest that either your tree is deformed or really big. There seems to be no problems with the code.
If your tree is deformed in such a way that you have a Node inserted twice in the same tree then this code will break.
Added - You are eating a little more stack than you need - replacing p.left.getHeight() with getHeight(p.left) etc. would avoid one stack push per recursion. If your issue is merely big tree then this might scrape you through but this would only postpone the problem.
From looking at both getHeight methods, it seems like you don't have a tree but a cyclic graph. You should start testing with a tree consisting of only the root and then add nodes until you observe the infinite recursion. You probably have an error in the function that rebalances the tree.
EDIT: And you should make the attributes (at least left and right) private.
I've had a go, and it works for the left subtree but not the right.
I'm close but my logic is wrong, can anyone help correct and explain the logic to this.
public static MyNode preOrderNumbering(MyNode n) {
if (n != null) {
n.obj = 0; // Set root decoration to 0;
preOrderHelper(n, 1); // Set decorations according to preorder.
}
return n;
}
public static MyNode preOrderHelper(MyNode n, int counter) {
if (n != null) {
if (n.left != null) {
n.left.obj = counter++; // Set the left object decoration to current count + 1;
preOrderHelper(n.left, counter);
}
if (n.right != null) {
n.right.obj = counter++; // Set the left object decoration to current count + 1;
preOrderHelper(n.right, counter);
}
}
return n;
}
Before:
After:
You need to update the counter with everything that's discovered on the left before going to the right.
Something like this:
public static int preOrderNumbering(MyNode n, int count){
if(n != null){
n.obj = ++count;
count = preOrderNumbering(n.left, count);
count = preOrderNumbering(n.right, count);
}
return count;
}
You're passing counter by value, not by reference (because that's how Java works), so when the recursion unwinds, so will the counter.
You could update the counter by returning the current value from the recursive call.