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

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.

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.

Remove recursively from a binary search tree

This is homework; please don't just give me code
I have two methods: remove(T data) and removeRec(Node<T> node, T data).
In its current state, it seems my code only removes the root node of the BST.
#Override
public T remove(T data) {
if (data == null) {
throw new IllegalArgumentException("Data is null");
}
if (root == null) {
throw new java.util.NoSuchElementException("BST is empty");
} else {
size--;
BSTNode<T> dummy = new BSTNode<T>(null);
return removeRec(root, data, dummy).getData(); //This is probably wrong too
}
}
/**
* Helper method to recursively search for, and remove the BSTNode with
* the given data in it
* #param node is the node we're currently at
* #param data is the data we're looking for
* #param temp I have no idea why
* #return node that was removed
*/
private BSTNode<T> removeRec(BSTNode<T> node, T data, BSTNode<T> temp) {
if (compare(data, node.getData()) < 0) {
temp.setLeft(removeRec(node.getLeft(), data, temp));
} else if (compare(data, node.getData()) > 0) {
temp.setRight(removeRec(node.getRight(), data, temp));
} else if (node.getLeft() != null && node.getRight() != null) {
temp.setData(findMin(node.getRight()).getData());
temp.setRight(removeRec(node.getRight(), data, temp));
} else {
if (node.getLeft() != null) {
temp = node.getLeft();
} else {
temp = node.getRight();
}
}
return temp;
}
private int compare(T a, T b) {
return a.compareTo(b);
}
My instructor has told me (as a hint) that I should see what passing in a third argument into the method, in this case, BSTNode<T> temp. I don't understand how that helps though, or how to utilize it. I don't see how using a third argument helps; and I can't find anything online as to why you'd do this either.
There are three main possibilities when you try to remove data from your Binary Search Tree:
data is less than the current node value: Call remove on the left subtree or throw a NoSuchElementException if it is null.
data is greater than the current node value: Call remove on the right subtree or throw a NoSuchElementException if it is null.
data is equal to the current node value.
1 and 2 are pretty straightforward, but 3 has four more cases to consider:
3.1. current node is a leaf: Both left and right subtrees are null. Just replace the reference to the current node in its parent by null.
3.2. current node has only the left child: You need to make the parent of the current node point to the left subtree, thus removing the current point. To do this, you can implement a function that will check if the current point was on the left or right subtree of the parent and replace it accordingly. Calling it would look like this:
replaceNodeInParent(node, node.getLeft(), parent);
3.3. current node has only the right child: Similar to 3.4, but using getRight() instead of getLeft().
3.4. current node has both the left and right children: You should maintain the property of the BST that all nodes on the left are less than the current node and all nodes on the right are greater than the current node. To do so, you should find the smallest value on the right, copy it to the current node, and delete it from the right subtree. Something like this:
BSTNode<T> successor = findMin(node.getRight());
node.setData(successor.getData());
removeRec(node.getRight(), successor.getData(), node);
It looks like your BSTNode doesn't hold a reference to the parent node. If so, I believe that's what the third argument for removeRec should be. You will need a reference to the parent every time you replace the current node, so you can set the parent left or right subtree as needed.
For further reading, you can check this article on Binary Search Trees from Wikipedia.

Deleteing a Node in a Binary tree

Iunderstand the basis of a deletion algorithm in a Binary Search tree and have created the following code to delete the largest value from the tree.
public void DelLargest()
{
Node<T> del = this;
boolean child = this.left.empty();
boolean child2 = this.right.empty();
right.DelLargest();
if(child && child2)
this.head = null;
else if(child == true && child2 == false)
this.head = left;
}
Basically what I have is that the recursion runs until 'this' is the rightmost node and then checks two cases, whether 'this' is a leaf, or whether 'this' has a left child. (The other case normally associated with this kind of algorithm is redundant because in finding the node with the largest value, I have gone as right as I can go.) The trouble I am having is getting the current node to then either point to null or to the value at Node left.
Note : This is what my instructor referred to as a "modern" Binary search Tree wherein a vertex or "filled" node and a nil or "empty" node are two subclasses of Interface Node which define the characteristics of each type.
I've managed to narrow the problem down to the fact that I do not have a method that returns a value of a given Node. Working on that now, input would be appreciated.
As suggested in the other answer you should use iterative approach.
In a BST the largest value is the rightmost node.
So do a scan and keep going right until you hit a null.
In the scan keep track of three nodes. (gpnode, pnode, node).
Once the scan is done you will have (gpnode,pnode,null)
Now there are 2 cases.
case 1:
pnode is a leaf. So change the edge (gpnode,pnode) to (gpnode,null)
case 2: (EDITED)
pnode.lChild is not null. Note that pnode.rChild will be null as the search would have terminated at that point.
Now change the edge (gpnode,pnode) to (gpnode,pnode.lChild)
Here is the pseudo code:
public class Node
{
long key;
Node lChild;
Node rChild;
}
public void DelLargest()
{
Node gpnode = null;
Node pnode = null;
Node node = root;
while(node != null) // keep going right until a null is reached
{
gpnode = pnode;
pnode = node;
node = node.rChild;
}
if(pnode.lChild == null) // its a leaf node So change the edge (gpnode,pnode) to (gpnode,null)
{
if(gpnode.lChild == pnode)
{
gpnode.lChild = null;
}
else
{
gpnode.rChild = null;
}
}
else // copy lChild's key to this node and delete lChild
{
if(gpnode.lChild == pnode)
{
gpnode.lChild = pnode.lChild;
}
else
{
gpnode.rChild = pnode.lChild;
}
}
}
You've got the right idea. What you want to do is keep a reference to the right most nodes parent, and the right most nodes left child so then when you delete it you can attach the two.
Here's an iterative solution. This will generally be more efficient than recursion, but if you want recursion you should be able to adapt it:
public void delLargest() {
// get rightmost node's parent
Node<T> current = root;
while(current.right != null && current.right.right != null) {
current = current.right;
}
// get the rightmost nodes left node
Node<T> left = current.right.left;
// attach the parent and left
current.right = left;
// nothing points to the right most node anymore, so it will be garbage collected
}

Setting the left attribute of a binary tree node through a for loop

I have a binary tree, being implemented through linked nodes (with: int element, BinaryNode left, BinaryNode right, BinaryNode parent attributes). And I want to set the the nth node from the root to, say, x. i.e. if n = 3, then I want to do root.left.left.left = x.
Initially, I was thinking of setting a temporary BinaryNode variable and doing the following:
BinaryNode temp = root;
BinaryNode x = new BinaryNode(10, null, null, null);
for (int i = 0; i < n; i++){
temp = temp.left;
}
And then once I got to the end, I wanted to say, temp.left = x, but then I realized/thought that doing that wouldn't do anything to the actual tree, since temp is just a copy of the root?
So I how would I go about trying to do this? :<
In your example, temp is not a copy of the root. It is a reference to the root. Therefore, your code would in fact modify the original tree. However, you have another problem-- what if there aren't that many nodes in the tree? In other words, if n is 5 and there are only three nodes in the .left chain of nodes, then you will end up getting a NullPointerException. You need to be checking for null as you go down the chain.
I think this should work
private void replace(BinaryNode node, int n, int x) {
if (n==0) {
node.x = x
return;
}
if (node.left != null)
replace(node.left, n-1, x);
}
And call it with replace(root, 3, 10) for instance

Edit the nodes in a binary tree in Java

Okay. I have a binary tree, and this is what I want to do with it:
For each node in original tree:
If it's not a leaf, replace it with a leaf node.
Do a calculation on the original tree updated with the removed branch.
Revert the node back to how it was (so now the tree is the same as at the beginning).
The problem is this: I am traversing the tree using a stack. If I change the stack.pop() node to a leaf, this does NOT remove any branches in the original tree. It's the same reasoning behind why you can do:
int x=1
int y=x
y++
And x still equals 1. There's a technical term for this but I forgot it.
So how can I edit the nodes in an original tree and still traverse it?
This is basically what I'm doing to traverse the tree right now:
public void iterativePreorder(Node root) {
Stack nodes = new Stack();
nodes.push(root);
Node currentNode;
while (!nodes.isEmpty()) {
currentNode = nodes.pop();
Node right = currentNode.right();
if (right != null) {
nodes.push(right);
}
Node left = currentNode.left();
if (left != null) {
nodes.push(left);
}
//This is where you do operations on the currentNode
}
}
From what I can tell from your question, for every Node you want to calculate something about the tree as if that node was a leaf.
To do this there is no reason to actually make that node a leaf and then reattach it. Instead, your logic can simply remember which node to treat as a leaf for each computation.
Traverse the tree, and for each Node, let's call it outerCurrentNode, once again traverse the tree doing your calculation - but now for each Node, let's call it innerCurrentNode, test to see if outerCurrentNode == innerCurrentNode. If the test returns true, treat that innerCurrentNode as if it's a leaf, ignoring its children.
EDIT: Here's a mock up of what I'm suggesting (untested):
//entry point - called from directing code
public void iterativePreorder(Node root) {
iterativePreorderKernel(root, root);
}
//recursive method - keeps track of root in addition to current Node
private void iterativePreorderKernel(Node root, Node current) {
if (current.left() != null) {
iterativePreorderKernel(root, current.left());
}
if (current.right() != null) {
iterativePreorderKernel(root, current.right());
}
//for each Node in the tree, do calculations on the entire tree, pretending
//the current Node is a leaf
doCalculation(root, current);
}
//calculation method (also recursive) - takes a current Node, plus
//the Node to treat as a leaf
public void doCalculation(Node innerCurrent, Node pretendLeaf) {
//do calculation with inner current node
if (innerCurrent != pretendLeaf) {
if (innerCurrent.left() != null) {
doCalculation(innerCurrent.left(), pretendLeaf);
}
if (innerCurrent.right() != null) {
doCalculation(innerCurrent.right(), pretendLeaf);
}
}
}
I'm using recursion instead of a Stack, but either will work. iterativePreorder() does a traversal, calling doCalculation() for each Node, passing it in along with the root (to keep track of the entire tree). That method then does its own traversal, doing your calculation, but stopping short when it reaches the specially marked Node.

Categories