BST simple insertion with recursion - java

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.

Related

Leetcode208, different execution sequences lead to different results [duplicate]

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.

BST implementation java

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.

Adding node to tree - Why is the root not getting initialized?

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.

Assign "this" to a reference variable in Java

I'm trying to used recursive method to complete the addLast method in a singly linked list, however, the code gives me a wrong output of list.size() = 2 and list.getFirst() = 5. The reason should be due to the line
SLList p=this;
It seems changing p reference changes "this" reference as well, which is not so logic to me. Could anyone give some details about this? Thx
public class SLList {
public class IntNode {
public int item;
public IntNode next;
public IntNode(int i, IntNode n) {
item = i;
next = n;
}
}
private IntNode first;
public SLList(int x) {
first = new IntNode(x, null);
}
/** Adds an item to the front of the list. */
public void addFirst(int x) {
first = new IntNode(x, first);
}
/** Retrieves the front item from the list. */
public int getFirst() {
return first.item;
}
/** Adds an item to the end of the list. */
public void addLast(int x) {
SLList p = this;
if (p.first. next == null) {
p.first.next = new IntNode (x, null);
}
else {
p.first = p.first.next;
p.addLast(x);
}
}
/** Returns the number of items in the list using recursion. */
public int size() {
/* Your Code Here! */
SLList p = this;
if (p.first == null) {
return 0;
}
else if (p.first.next == null){
return 1;
}
else {
p.first = p.first.next;
return 1 + p.size();
}
}
public static void main (String[] args) {
SLList list=new SLList (5);
list.addFirst(10);
list.addFirst(15);
list.addLast(17);
System.out.println(list.getFirst());
System.out.println(list.size());
}
}
The problem is nothing to do with the assignment of this. Nothing can change this. Period.
(But things can change the state of the object that this refers to.)
The real problem is in your implementation of the size method. Your size method is causing the list to change. It shouldn't. In your case, the change causes:
the size() method to return the wrong value
subsequent getFirst() calls to return the wrong value.
I won't say exactly where the bug, but you should be able to spot it yourself by a process of elimination. (Or if that fails, use a debugger and try to observe where the list is changing.)
There are bigger problems with your algorithms than you think. size() is incorrect. You can fix this if you realize that you need to count the number of IntNode objects in the list. Similarly all other methods need to manipulate IntNode objects.
SLList p = this;
p reference to the same SLList object. if you make any changes to 'p' then it will also happened to 'this', becuase of reference type (not value type).
Here in the statement
p.first = p.first.next;
the reference to the first is changed when you call 'addLast' method. You loss the reference to the first item.
If you remove the line
list.addLast(17);
in main method you will see the correct answer. The problem is with this method.
Change the method as follow and add the new method below.
/** Adds an item to the end of the list. */
public void addLast(int x) {
addLast(x, this.first);
}
private void addLast(int x, IntNode node){
if(node.next == null){
node.next = new IntNode (x, null);
}else {
node = node.next;
addLast(x, node);
}
}
Then you will not lose the reference to first item and now it works fine,
Problem in your implementation is addLast and size method are changing the value of field variable first.
It don't matter whether you assignthis to some variable or use directly.
Because assigning this to some variable does not create new this object but assign's reference to that variable.
So you should first copy value of first field variable to some local variable then iterate on it.In this way your first will not change.
Hint: Don't change the first variable reference.
Your addLast() and size() changes value of first which is wrong.
Problem is in this line.
p.first = p.first.next;

Linked List Pointer Scope

How come when I create a linked list node, append some data, then move the head in another method, the head stays the same in the callee method?
For example:
public static void append(Node n, int d) {
while (n.next != null) {
n = n.next;
}
n.next = new Node(d);
}
public static void test(Node n) {
n = n.next;
}
public static void main(String[] args) {
Node head = new Node(5);
append(head, 4);
append(head, 3);
test(head); //this moves the head to head.next
//why is head still = 5 after we get here?
}
In the method append, the line n = n.next won't affect the original node passed as argument, in your case head. Why? Because Java is pass by value. This means that if inside the method, you modify the reference of head (which is received as n inside the method), it won't affect the original reference. Therefore, head will still refering to the same location in memory (the same object).
Also, your method test doesn't do anything because you are creating a local variable:
Node next = ...;
and then assigning n.next to it. But this variable only exists inside that method, so it won't affect anything outside it.
next is a property, not a method. Your test method is just grabbing the reference to n.next, it is not "moving the head."

Categories