I studied C++ , but I am quite new to Java.I am trying to write a binary search tree(BST) class.Here is my code:
public class binary_tree {
public class node
{
int data;
node left , right;
node(int data , node left , node right)
{
this.data = data;
this.left = left;
this.right = right;
}
}
private node root = null;
public void addElement(int x)
{
addElementNotSeen(x , this.root);
//this function allows the user to only give x
//as a parameter
}
private void addElementNotSeen(int x , node curent)
{
if (curent == null)
{
curent = new node(x , null , null);
}
else
{
if (x > curent.data)addElementNotSeen(x , curent.right);
else addElementNotSeen(x , curent.left);
}
}
}
However , my root seems to not get any value.I've seen that in Java you don't need to pass arguments by reference so I can't see the problem.Can you help me?
When you check if node is null and then create it, you are actually creating a new node for the reference of the parameter, not for the original reference that you passed outside the function.
So, instead of checking if node is null in addElementNotSeen, check if this.root is null in addElement, and instantiate directly.
public void addElement(int x)
{
if(this.root == null)
this.root = new node(x, null, null)
else
addElementNotSeen(x , this.root)
}
The same goes when passing recursively the data down your tree. Don't risk to pass null as a parameter. Check if left or right are null and if so, create them directly, as in the previous example with this.root.
private void addElementNotSeen(int x , node curent)
{
else
{
if (x > curent.data){
if (curent.right == null)
curent.right = new node(data, null, null);
else
addElementNotSeen(x , curent.right);
}else{
// the same for left
}
}
}
This line of code
curent = new node(x , null , null);
doesn't have any effect outside of addElementNotSeen since it modifies local reference only.
See Oracle docs:
Reference data type parameters, such as objects, are also passed into methods by value. This means that when the method returns, the passed-in reference still references the same object as before. However, the values of the object's fields can be changed in the method, if they have the proper access level.
Related
I have written two functions for adding a new node to a tree: one public and one private. The private one is recursive. The public one calls the recursive one. First question: Is this acceptable practice?
public void addNode(int val) {
addNode(val, root, null, 0);
System.out.println("Root is null: " + (root == null));
}
private void addNode(int val, Node node, Node parent,int height) {
if(node == null) {
node = new Node(val, height, 0);
System.out.println("is new node equal to root?"+(node == root));
System.out.println("Added node on height: " + node.getHeight());
return;
}
height++;
addNode(val, node.left, node, height);
addNode(val, node.right, node, height);
}
Now here is the problem: The root variable does not get initialized. It is declared in the tree class. public Node root;
This is very confusing to me since I am aware that Java is pass-by-reference, not pass-by-value. Why is root null after these functions have been called?
Console output:
is new node equal to root?false
Added node on height: 0
Root is null: true
If in Java code a function assigns a new value to a function parameter, this never impacts the variable that the caller may have passed as argument. You may have been confused with what happens when a parameter variable is mutated: for instance, if it is an object and you assign a different value to one of its properties, then this change is visible to the caller's object, since it really is the same object. But a plain assignment to a parameter will always have an effect on that local variable only.
To make it work, design your function to return the node that you provided to it (whether it got a new value or not).
There is another issue: you are currently adding a new node in both the left and right subtree (if they exist), and this repeats recursively. I will assume you were trying to insert values in a binary search tree, and so you should choose in which subtree you will add the node.
Finally, it is not needed to pass parentNode or height as argument, since you seem to store the height in each node, and so you know that the new node's height must be one more than the height stored in its parent node (or, when absent, 0).
public void addNode(int val) {
root = addNode(val, root);
}
private void addNode(int val, Node node) {
if (node == null) {
return new Node(val, 0, 0); // NB: height will be updated when backtracking
}
if (val < node.val) {
node.left = addNode(val, node.left);
node.left.height = node.height + 1;
} else {
node.right = addNode(val, node.right);
node.right.height = node.height + 1;
}
return node;
}
Finally, the name "height" is a bit misleading here, as this term is supposed to denote the height of the (sub)tree of which the node is the root. But height in this code represents the depth of the node in the tree. See What is the difference between tree depth and height?.
Of course there is nothing wrong in calling private method (even recursive) from public method.
Root is null simply because you are assigning new value to node argument, you are not changing object, but you are creating new one.
following
private void addNode(int val, Node node, Node parent,int height) {
...
node = new Node(val, height, 0);
will not change argument node in caller. So after calling
addNode(val, root, null, 0);
root stays unchanged (with null value)
Also keep in mind objects are passed by value in Java.
Actually (inside Java) in function you receive only memory address (value) for node (e.g. 000000D5098FFA70 in x64 arch). So if you modify e.g. node.left you are actually changing memory at address 000000D5098FFA70 + 4. However if you change
that address - value - you lose access to this object. And from that moment you are working only with local variable. This is why it is called passed by value.
I have written two functions for adding a new node to a tree: one public and one private. The private one is recursive. The public one calls the recursive one. First question: Is this acceptable practice?
public void addNode(int val) {
addNode(val, root, null, 0);
System.out.println("Root is null: " + (root == null));
}
private void addNode(int val, Node node, Node parent,int height) {
if(node == null) {
node = new Node(val, height, 0);
System.out.println("is new node equal to root?"+(node == root));
System.out.println("Added node on height: " + node.getHeight());
return;
}
height++;
addNode(val, node.left, node, height);
addNode(val, node.right, node, height);
}
Now here is the problem: The root variable does not get initialized. It is declared in the tree class. public Node root;
This is very confusing to me since I am aware that Java is pass-by-reference, not pass-by-value. Why is root null after these functions have been called?
Console output:
is new node equal to root?false
Added node on height: 0
Root is null: true
If in Java code a function assigns a new value to a function parameter, this never impacts the variable that the caller may have passed as argument. You may have been confused with what happens when a parameter variable is mutated: for instance, if it is an object and you assign a different value to one of its properties, then this change is visible to the caller's object, since it really is the same object. But a plain assignment to a parameter will always have an effect on that local variable only.
To make it work, design your function to return the node that you provided to it (whether it got a new value or not).
There is another issue: you are currently adding a new node in both the left and right subtree (if they exist), and this repeats recursively. I will assume you were trying to insert values in a binary search tree, and so you should choose in which subtree you will add the node.
Finally, it is not needed to pass parentNode or height as argument, since you seem to store the height in each node, and so you know that the new node's height must be one more than the height stored in its parent node (or, when absent, 0).
public void addNode(int val) {
root = addNode(val, root);
}
private void addNode(int val, Node node) {
if (node == null) {
return new Node(val, 0, 0); // NB: height will be updated when backtracking
}
if (val < node.val) {
node.left = addNode(val, node.left);
node.left.height = node.height + 1;
} else {
node.right = addNode(val, node.right);
node.right.height = node.height + 1;
}
return node;
}
Finally, the name "height" is a bit misleading here, as this term is supposed to denote the height of the (sub)tree of which the node is the root. But height in this code represents the depth of the node in the tree. See What is the difference between tree depth and height?.
Of course there is nothing wrong in calling private method (even recursive) from public method.
Root is null simply because you are assigning new value to node argument, you are not changing object, but you are creating new one.
following
private void addNode(int val, Node node, Node parent,int height) {
...
node = new Node(val, height, 0);
will not change argument node in caller. So after calling
addNode(val, root, null, 0);
root stays unchanged (with null value)
Also keep in mind objects are passed by value in Java.
Actually (inside Java) in function you receive only memory address (value) for node (e.g. 000000D5098FFA70 in x64 arch). So if you modify e.g. node.left you are actually changing memory at address 000000D5098FFA70 + 4. However if you change
that address - value - you lose access to this object. And from that moment you are working only with local variable. This is why it is called passed by value.
When i add a node for example, named "Bob" in the insert method:
public void insert(String aLabel){
//left recursion:
if(this.getLabel().compareTo(aLabel) <= 0) {
if (childrenLeft == null) {
BSTreeNode aNode = new BSTreeNode(aLabel,this);
return;
}
else {
childrenLeft.insert(aLabel);
}
}
//right recursion
else {
if (childrenRight==null) {
BSTreeNode aNode = new BSTreeNode(aLabel,this);
return;
}
else{
childrenRight.insert(aLabel);
}
}
}
my tree only adds a blank node with no label on the left side of the treee only. is there something wrong with the (BSTreeNode aNode = new BSTreeNode;)? because when i hard code the nodes like:
BSTreeNode Louis = new BSTreeNode("Louis", treeRoot);
BSTreeNode bonny = new BSTreeNode( "bonny", treeRoot);
BSTreeNode Sue = new BSTreeNode("Anne", bonny);
BSTreeNode Sam = new BSTreeNode("Sam",Louis);
BSTreeNode Anne2 = new BSTreeNode( "delta", bonny);
BSTreeNode Frank = new BSTreeNode("Kalle", Louis);
the tree shows both a label and is inserted at the desired location.
other code-
the constructor:
public BSTreeNode( String aLabel,BSTreeNode aParent){
label = aLabel;
parent = aParent;
//add this node as a child of this node's parent either left or right
if(parent != null){
if(parent.getLabel().compareTo(label)<= 0) {
parent.addLeftChild(this);
}
if(parent.getLabel().compareTo(label)> 0) {
parent.addRightChild(this);
}
}
}
this is the constructor that adds the node to the parent when a node is created.
add childleft and right methods:
private void addLeftChild(BSTreeNode aNode){
if(childrenLeft == null) this.childrenLeft = aNode;
}
private void addRightChild(BSTreeNode aNode) {
if(childrenRight == null) this.childrenRight = aNode;
}
Most binary trees follow a different style, and set the parent's left/right child inside the recursive method, instead of the child going up and telling somebody it's their new parent
This code is a bit more standard for how most binary trees function:
public void insert(String aLabel)
{
if(getLabel().compareTo(aLabel) <= 0)
if(childrenLeft == null)
childrenLeft = new BSTreeNode(aLabel, this);
else
childrenLeft.insert(aLabel);
else
if(childrenRight == null)
childrenRight = new BSTreeNode(aLabel, this);
else
childrenRight.insert(aLabel);
}
That code should correctly save the values of the BSTreeNodes being created, and has the added effect of being less confusing as to how a parent is getting it's child
It makes a whole lot more sense to most people for a parent to be getting a child, instead of a child reaching up to a node and telling it that it's the new child on the block
You logic might be a little flawed.
When adding from your constructor, you're calling addLeftChild or addRightChild directly. These function check if the node on the right/left is null or not and adds the value if its null. But what if it's not null. It should then compare to the left/right child and continue because otherwise the nodes do not get added (i.e. the function falls through & doesn't return anything as its void).
I am trying to write a small function to insert a node to a BST. The "insert" function is working correctly. I changed it to "insert2", where it doesn't work. I cannot figure out why it doesn't work. What is the difference between "insert" and "insert2" in runtime?
Insert method
public void insert(Node node, int x) {
if (x < node.val) {
if (node.left == null) node.left = new Node(x);
else insert(node.left, x);
} else {
if (node.right == null) node.right = new Node(x);
else insert(node.right, x);
}
}
insert2 method
public void insert2(Node node, int x) {
if (node == null) {
node = new Node(x);
return;
}
if (x < node.val) insert2(node.left, x);
else insert2(node.right, x);
}
Definition of Node
public class Node {
int val;
Node left, right;
public Node (int _val) {
this.val = _val;
}
}
Thanks in advance.
Java is a pass by value language. This means that when you pass a variable to a method, either primitive or objects, the method cannot change that variable since it doesn't know anything about that variable. The method has it's own variable and assigning anything new to a argument variable only persist in that scope and does not mutate other bindings or objects.
When you do have:
public static void update(String str) {
str = "changed";
}
And do:
String s = "hello";
update(s);
System.out.println(s);
It will print "hello" since although the address of "hello" was passed to update, update only update the local variable to the address of a new string. Assignment never changed the variable that was used to apply the method or the objects both variables points to.
String h = "hello";
String w = "world";
String x = h; // x points to the same string as h
x = w; // x got it's value changed to point to w
System.out.println(h + " " + w);
The last statement prints "hello world", not "world world" as if assignment mutated the previous object.
So what happened in your insert methods?
insert2 overwrites a local variable that happens to be null to a new Node, but it has nothing to do with the original argument passed. The newly created node is only accessible from that scope so when it returns the new node is ready to be garbage collected. The tree passed to the original method was never mutated thus it never gets a new value.
If you look at insert it takes a not null Node and alters right or left property on either that node or one of it's descendants. Thus when you inspect the original argument the tree has changed since it didn't mutate arguments, but the object itself.
Mutating objects is not the same as mutating variables.
im coming from c++ to java and i am confused on binary trees with java. is the only way to have a Node class is to make it an inner static class? all the examples i see do this. However, the way im doing it is i have a node class and a binarytree class uses this node class. but i keep getting an error when i try inserting into the tree after the second insert. i get an exception at this line if(dataIn <= nodeIn.getLeft().getData()){
I am confused as to what i did wrong.... here is my code for insert that i have. thanks in advance..
public void insert(int dataIn){
root = insert(root, dataIn);
}
private Node insert(Node nodeIn, int dataIn){
if(nodeIn==null){
nodeIn = new Node(null, null, dataIn);
}else{
if(dataIn <= nodeIn.getLeft().getData()){
nodeIn.setLeft(insert(nodeIn.getLeft(), dataIn));
}else{
nodeIn.setRight(insert(nodeIn.getRight(), dataIn));
}
}
return nodeIn;
}
Part of the reason it's confusing is that "Node" should not be a parameter to the insert method, you should be calling an insert method defined in node.
So let's say you hold the "Root" node in your "normal code"--let's call it "rootNode" just to be obscure.
Okay, so your code to insert into the tree would be:
rootNode.insert(newValue);
Easy enough.
Now to define that method.
public class Node {
private int value;
private Node lower;
private Node higher;
public void insert(int newValue) {
if (newValue < value)
if(lower == null)
lower=new Node(value);
else
lower.insert(newValue);
else
if(higher == null)
higher=new Node(value);
else
higher.insert(newValue);
}
// and you'll need a constructor
public Node(int value) {
this.value=value;
}
}
This should read much more clearly. I'm going to hit "Post" then I'm going to edit it and figure out how to easily refractor that evil evil copy & paste code.
On second thought, I'll leave it there because it's more readable. The best fix I can see is to make the nodes an array, then you get:
public class Node {
private int value;
private Node[] nodes=new Node[2];
private final int LOWER=0;
private final int HIGHER=1;
public void insert(int newValue) {
int index=LOWER;
if (newValue > value)
index=HIGHER;
if(nodes[index] == null)
nodes[index]=new Node(value);
else
nodes[index].insert(newValue);
}
}
But I won't replace the original because, as I said, it's clearer.
I recommend the refactoring book for more of this. It really does help simplify your code once you really get OO. Passing an object to an otherwise static method (one that doesn't use member variables) is a dead give-away.
With more considerations about #ted's comment and OO--getLeft and getRight shouldn't even be an issue. Neither is necessary outside the abstraction.
In general what you probably need is these methods in Node:
public boolean doesContain(int value) {
if(value == this.value)
return true
else
return nodes[ this.value < value ? LOWER : HIGHER].doesContain(value);
}
and maybe
public void getValuesSorted(LinkedList l) {
nodes[LOWER].getValuesSorted(l);
l.put(value);
nodes[HIGHER].getValuesSorted(l);
}
Then you don't even need to expose that it's a tree you are dealing with--beter OO abstraction.
You need to test whether nodeIn.getLeft() == null.
is the only way to have a Node class is to make it an inner static class? all the examples i see do this.
No.
The Node class doesn't need to be an inner or nested class. (Strictly speaking a "static inner" class is a nested class.)
However, only an inner or nested class can be private. If your Node class is a regular class you are exposing implementation details to (at least) other classes in the same package. This is a bad idea ... and that explains why you see the Node class declared the way it is.
you can use the below code to clear your understanding. Mostly we do not use any other class everything can be done inside a single Node class itself. here is a very basic example which i believe might be helpful... Also when i see you have two methods in which you have only provided data rather than root. Trust me it is better to have root in your main class. reasoning we have it handy where ever we need to cache.
public Node insert(Node root, Node newNode) {
if (root == null) {
root = newNode;
} else {
if(root.data > newNode.data) {
root.left = insert(root.left, newNode);
} else {
root.right = insert(root.right, newNode);
}
}
return root;
}
directly call this method from any class where you have initialized. here is a sample plz check..
Node root = new Node(10);
root.insert(root, new Node(5));
root.insert(root, new Node(3));
root.insert(root, new Node(6));
root.insert(root, new Node(1));
Instead of:
if (dataIn <= nodeIn.getLeft().getData()) {
... you want:
if (dataIn < nodeIn.getData()) {
You need to compare the value that is to be inserted with the value at the current node.
I changed the <= sign to a < sign so that duplicates are avoided.
So your code refactored is:
public void insert(int dataIn) {
root = insert(root, dataIn);
}
private Node insert(Node nodeIn, int dataIn){
if (nodeIn == null) {
nodeIn = new Node(null, null, dataIn);
} else {
if (dataIn < nodeIn.getData()) {
nodeIn.setLeft(insert(nodeIn.getLeft(), dataIn));
} else {
nodeIn.setRight(insert(nodeIn.getRight(), dataIn));
}
}
return nodeIn;
}
Actually, for a binary tree there is no need to check whether the inserting element is either greater than or left than the parent node. I think there is no need of checking those conditions. We have to take the input from user whether to insert right or left.
public class TreeNode
{
private int data;
private TreeNode left;
private TreeNode right;
public Tree( )
{
data = 0;
left = null;
right = null;
}
public Tree( int initialInfo, TreeNode initialLeft, TreeNode initialRight )
{
data = initialInfo;
left = initialLeft;
right = initialRight;
}
public void setLeft( TreeNode newLeft )
{
left = newLeft;
}
public void setRight( TreeNode newRight )
{
right = newRight;
}
public void insert( int element )
{
if( element <= data )
{
if( left == null )
setLeft( new TreeNode( element, null, null ) );
else
left.insert( element );
}
else
{
if( right == null )
setRight( new TreeNode( element, null, null ) );
else
right.insert( element );
}
}
}
All your solutions do not take into accoount what would happen if the node was to be inserted in the middle of the tree. You are assuming that it will also be smallest or greatest. When you insert something in the middle of the tree, then you will realize that the operation becomes more complex.