Binary search tree recursive insert - java

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.

Related

Binary Search Tree, Height Method not returning correct values

Java Binary Search trees, Height method not returning the correct height. I tried a lot of stuff on the internet, none of it worked. If someone can point out the issue with my code it will be very helpful.
#Override
public int height()
{
return heightRecursive(root);
}
private int heightRecursive(Node node) {
if(node == null || (node.right == null && node.right == null)) return 0;
int lh=0;
int rh=0;
if(node.left!=null) {
lh=heightRecursive(node.left);
System.out.println(lh);
}
if(node.right!=null) {
rh=heightRecursive(node.right);
}
//System.out.println("--------"+1+Math.max(lh, rh));
return 1+Math.max(lh, rh);
}
The Add() Method is below:
#Override
public boolean add(Comparable e)
{
addR(e,root);
return true;
}
public Node addR(Comparable e,Node n)
{
//System.out.println("Null --"+n+" "+e+" "+size);
if(n==null)
{
n=new Node(e);
size++;
System.out.println("final --"+n+" "+e+" "+size);
}
else if(e.compareTo(n.value)<0)
{
n.left = addR(e,n.left);
//size++;
}
else if(e.compareTo(n.value)>0)
{
n.right = addR(e,n.right);
//size++;
}
return n;
}
I can't seem to get to the root cause of this, after inputting the debug printline code in add method, this is the output:
A final --trees.SimpleTreeSet$Node#58c1670b A 1
B final --trees.SimpleTreeSet$Node#6b57696f B 2
R final --trees.SimpleTreeSet$Node#44c8afef R 1
W final --trees.SimpleTreeSet$Node#12f41634 W 1
O final --trees.SimpleTreeSet$Node#262b2c86 O 1
final --trees.SimpleTreeSet$Node#5ed828d A 1
Y final --trees.SimpleTreeSet$Node#7a3d45bd Y 1
The main problem is in your add method: the root will never change. So if you add a first node to an empty tree, that new node is lost: it is returned by addR, but your code ignores it.
So:
public boolean add(Comparable e)
{
root = addR(e,root);
return true;
}
Another problem concerns the base case of the height recursion:
if(node == null || (node.right == null && node.right == null)) return 0;
You should check node.left (not twice node.right) and also distinguish between a null and a node. There is a height difference between those two cases. When a single-node-tree has height 0, then an empty tree should have height -1.
Corrected:
if (node == null) return -1;
if (node.left == null && node.right == null)) return 0;

Getting nth item of a BST

I am trying to return the data held by the nth item of a BST, I'm trying to do an inorder traversal with a counter, and when the counter is larger than n, return the current node. My current code seems to always return the first item, and I can't see where my logic is wrong. I only wrote the nth and inOrder methods, the rest were provided. I think I'm incrementing my counter too often, is that the cause or am I doing something else wrong. I'll post the main method I'm testing with below as well.
import java.util.NoSuchElementException;
public class BST {
private BTNode<Integer> root;
public BST() {
root = null;
}
public boolean insert(Integer i) {
BTNode<Integer> parent = root, child = root;
boolean goneLeft = false;
while (child != null && i.compareTo(child.data) != 0) {
parent = child;
if (i.compareTo(child.data) < 0) {
child = child.left;
goneLeft = true;
} else {
child = child.right;
goneLeft = false;
}
}
if (child != null)
return false; // number already present
else {
BTNode<Integer> leaf = new BTNode<Integer>(i);
if (parent == null) // tree was empty
root = leaf;
else if (goneLeft)
parent.left = leaf;
else
parent.right = leaf;
return true;
}
}
public int greater(int n) {
if (root == null) {
return 0;
}
else {
return n;
}
}
int c = 0;
public int nth(int n) throws NoSuchElementException {
BTNode<Integer> node = null;
if (root == null) {
throw new NoSuchElementException("Element " + n + " not found in tree");
}
else {
if (root != null){
node = inOrder(root, n);
}
}
return node.data;
}
public BTNode inOrder(BTNode<Integer> node, int n) {
c++;
while (c <= n) {
if (node.left != null) {
inOrder(node.left, n);
}
c++;
if (node.right != null) {
inOrder(node.right, n);
}
}
return node;
}
}
class BTNode<T> {
T data;
BTNode<T> left, right;
BTNode(T o) {
data = o;
left = right = null;
}
}
public class bstTest {
public static void main(String[] args) {
BST tree = new BST();
tree.insert(2);
tree.insert(5);
tree.insert(7);
tree.insert(4);
System.out.println(tree.nth(2));
}
}
An invariant you should consider is that when n = sizeOfLeftSubtree + 1, then return that node. If n is less, then go left. If n is greater, then go right and reduce n by sizeOfLeftSubtree+1. Note that I map n=1 to the first element (the leftmost element).
You could trivially calculate the size of a subtree recursively, or you can store the size at every root (every node is a root of a subtree) modifying you insert method (save in a stack/queue all nodes visited and if a new node is added just increment all sizes by 1).
If the size is stored the complexity will be O(log n). If not if could become O(n^2).
public int nth(int n) throws NoSuchElementException {
if( sizeOfTree(this.root) < n || n < 1)
throw new NoSuchElementException("Element " + n + " not found in tree");
BTNode<Integer> root = this.root;
boolean found = false;
do{
int sizeOfLeftSubtree = sizeOfTree(root.left);
if( sizeOfLeftSubtree + 1 == n ){
found = true;
}else if( n < sizeOfLeftSubtree+1 ){
root = root.left;
}else if( sizeOfLeftSubtree+1 < n ){
root = root.right;
n -= sizeOfLeftSubtree+1;
}
}while( !found );
return root.data;
}
public int sizeOfTree(BTNode<Integer> root){
if( root == null )
return 0;
else
return sizeOfTree(root.left) + 1 + sizeOfTree(root.right);
}
You don't change node in the inOrder method.
public BTNode inOrder(BTNode<Integer> node, int n) {
c++;
while (c <= n) {
if (node.left != null) {
// **** Add this - or something.
node = inOrder(node.left, n);
}
c++;
if (node.right != null) {
// **** Add this - or something.
node = inOrder(node.right, n);
}
}
return node;
}
Not suggesting this is the bug you are trying to fix but it is certainly a problem with the code.

Why does this method cause an Infinite Recursive call?

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.

Creating Java binary search tree

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.

AVL Tree Traversal, Search problems

I am having a few problems in my AVL tree implementation.. The code for all the rotations and the adding all seem to be correct and I dry-run the program to thoroughly check that it is running logically correct. I seem to be having a problem in my tree traversal (in-order) because it only outputs a few integers from the supposed 100. Also the search is always failing, regardless of what I enter. I cannot seem to grasp what is going on but I suspect that it has something to do with a few null pointers. Below is the code for the AVL tree, I am wondering if there's any incorrect code in the AddNode method or the rotation methods but they seem to be fine.. The classes are Node class, AVL class and AVL tree class which is the main class.
Node class
private int data;
private Node left;
private Node right;
private int height;
public Node(int m) {
data = m;
left = null;
right = null;
height = 0;
}
public void setToleft(Node newleft) {
left = newleft;
}
public Node getleftNode() {
return left;
}
public void setToright(Node newright) {
right = newright;
}
public Node getrightNode() {
return right;
}
public int getData() {
return data;
}
public int getHeight(){
return height;
}
public void setHeight(int height){
this.height = height;
}
AVL class
public Node root;
public AVL(int root) {
this.root = new Node(root); // since root presently has no left or right children, height is currently 0
}
public int Height(Node n) {
if (n == null) { //basis step
return -1;
} else { //add one for every path
if (n.getleftNode() == null && n.getrightNode() == null) {
return 0;
}
return 1 + Math.max(Height(n.getleftNode()), Height(n.getrightNode()));
}
}
public void add(int data) {
addNode(data, root);
root.setHeight(Math.max(Height(root.getleftNode()), Height(root.getrightNode())) + 1);
}
public void addNode(int data, Node n) {
if (data < n.getData()) {
if (n.getleftNode() == null) {
n.setToleft(new Node(data));
} else {
addNode(data, n.getleftNode());
}
n.setHeight(Math.max(Height(n.getleftNode()), Height(n.getrightNode())) + 1);
if ((Height(n.getleftNode()) + 1) - (Height(n.getrightNode()) + 1) == Math.abs(2)) {
if (data < n.getleftNode().getData()) {
n = LLRotation(n);
} else {
n = LRRotation(n);
}
}
} else if (data >= n.getData()) { //>= also caters for duplicates and inserts them infront of same value
if (n.getrightNode() == null) {
n.setToright(new Node(data));
} else {
addNode(data, n.getrightNode());
}
n.setHeight(Math.max(Height(n.getleftNode()), Height(n.getrightNode())) + 1);
if ((Height(n.getrightNode()) + 1) - (Height(n.getleftNode()) + 1) == Math.abs(2)) {
if (data >= n.getrightNode().getData()) {
n = RRRotation(n);
} else {
n = RLRotation(n);
}
}
}
}
public Node LLRotation(Node n) { //single
Node n1 = n.getleftNode();
n.setToleft(n1.getrightNode());
n1.setToright(n);
n.setHeight(Math.max(Height(n.getleftNode()), Height(n.getrightNode())) + 1);
n1.setHeight(Math.max(Height(n1.getleftNode()), Height(n)) + 1);
//compares heights of left and right subtrees and gets max
//the above source code is of course vital since the node height must be resetted after rotations
//adding 1 at the end of the last two code lines is important since
//initially the height is only calculated from subtrees onwards
//same for single right rotation below
return n1;
}
public Node RRRotation(Node n) { //single
Node n1 = n.getrightNode();
n.setToright(n1.getleftNode());
n1.setToleft(n);
n.setHeight(Math.max(Height(n.getleftNode()), Height(n.getrightNode())) + 1);
n1.setHeight(Math.max(Height(n1.getrightNode()), Height(n)) + 1);
return n1;
}
public Node LRRotation(Node n) { //double
n.setToleft(RRRotation(n.getleftNode()));
return LLRotation(n);
}
public Node RLRotation(Node n) { //double
n.setToright(LLRotation(n.getrightNode()));
return RRRotation(n);
}
public void inOrderTraversal(Node n) {
if (n != null) {
inOrderTraversal(n.getleftNode()); //recursive call to the left subtree
System.out.println(n.getData()); //line which makes the actual node to display its data
inOrderTraversal(n.getrightNode()); //recursive call to the right subtree
}
}
public void traverse() {
inOrderTraversal(root); // can be called in main class to automatically traverse tree from its root
}
public int search(int x) {
try {
if (x == root.getData()) { //basis step
System.out.println("Item found!");
return x;
}
if (x < root.getData()) {
root = root.getleftNode();
return search(x);//recursive call
} else {
root = root.getrightNode();
return search(x);//recursive call
}
} catch (NullPointerException e) {
System.out.println ("Search failed!");
return 0;
}
}
Main Class
public static void main(String[] args) throws IOException {
Scanner s = new Scanner(System.in);
AVL tree = null;
int choice = 0;
System.out.println("AVL TREE");
System.out.println("\n Choose an option from the menu: ");
System.out.println("\n\t 1.) Create file of 100 integers");
System.out.println("\n\t 2.) Create the tree");
System.out.println("\n\t 3.) In-Order traverse and show tree");
System.out.println("\n\t 4.) Search for integer");
System.out.println("\n\t 5.) Quit");
while (choice != 5) {
System.out.print("\nChoice: ");
choice = s.nextInt();
switch (choice) {
case 1:
createFile();
break;
case 2:
try {
FileReader readto = new FileReader("Integers.txt");
BufferedReader br = new BufferedReader(readto);
String line = br.readLine(); //reads text at start of file
line = br.readLine(); // skipping empty lines
line = br.readLine();
line = br.readLine();
int root = Integer.parseInt(line); //extracts first integer from the line
System.out.println("Root: " + root);
tree = new AVL(root);
int x = 0;
while (x != 99) {
try {
line = br.readLine();
int next = Integer.parseInt(line);
tree.add(next);
System.out.println(next);
x++;
} catch (NumberFormatException e) {
};
}
System.out.println("Tree successfully populated!");
} catch (FileNotFoundException e) {
System.out.println("ERROR: File not found!");
}
break;
case 3:
System.out.println("In-Order traversel executed. The now balanced tree shall now be printed in");
System.out.println("ascending order and also the left and right children of each node shall be printed.\n");
System.out.println("Traversal: ");
tree.traverse();
break;
case 4:
System.out.print("Please enter the integer to be searched: ");
int x = s.nextInt();
System.out.println(tree.search(x));
break;
case 5:
System.exit(0);
break;
default:
System.out.println("ERROR: Choice out of bounds!");
}
}
}
static void createFile() throws IOException {
Random r = new Random();
File intfile = new File("Integers.txt");
FileWriter writeto = new FileWriter("Integers.txt");
BufferedWriter bw = new BufferedWriter(writeto);
if (!(intfile.exists())) {
System.out.println("ERROR: File not found!");
} else {
bw.write("The following integers are randomly generated");
bw.newLine();
bw.write("and will be used to construct the AVL tree:");
bw.newLine();
bw.newLine();
int x;
System.out.println("The following random numbers shall be used to build the AVL tree: \n");
for (int i = 0; i < 100; i++) {
x = r.nextInt(100) + 1;
bw.write(String.valueOf(x));
bw.newLine();
System.out.println(x);
}
bw.close();
}
}
The output for the traversal is just the following:
Traversal:
44
53
54
54
77
Suppose that there were 100 integers entered and among them were these. But the output for the traversal was only this.
Output for the search is like this:
Choice: 4
Please enter the integer to be searched: 44
Item found!
44
Choice: 4
Please enter the integer to be searched: 100
Search failed!
0
100 and 44 were both integers added to the tree, but 44 was found and 100 wasn't.. I don;t understand..
Anyone can guide me to a solution..?
Thanks in advance :)
Well, first the obvious thing... In your search method, you are abusing the root variable, which holds the root of your tree, setting it to new values as your search proceeds. So, after the first search, root points to the last node traversed in the search and no longer to the root node of the tree. All following searches are unlikely to find anything at all from that point on.
As your search is recursive, try passing on the node-to-be-searched-in as parameter:
int search(Node node, int key) {
if (node == null) {
return 0; // missing from tree
} else if (key < node.getData()) {
return search(node.getLeft(), key);
} else if (key > node.getData()) {
return search(node.getRight(), key);
} else {
return node.getData(); // found it
}
}
(Edited to address the comments) You might have to expose this method like you do with your add/addNode method pair using a publicly available wrapper, and an internal implementation:
public int search(int key) {
return searchNode(root, key);
}
private int searchNode(Node node, int key) {
// Perform the recursive search, as above
}
There are other problems related to your add/addNode methods. Maybe I just overlooked it, but nowhere do you adjust the root node of your tree, if rotation would make it necessary. This, in effect, causes your tree to get out of balance, losing the AVL property over time.

Categories

Resources