Should implicit reference be preferred over explicit - java

For example, in the function below, which adds a node to BST, I have used only implicit references.
I could have declared explicitly a variable TreenNode node = .., at start of function and used it where appropriate. Now, I am not aware if it is opinion based or not. Sincerely is there any pros or or con of using OPTION1 over OPTION2
OPTION1:
public void add(int item) {
if (root == null) {
root = new TreeNode(null, item, null);
return;
}
TreeNode node = root;
while (true) {
if (item < node.item) {
if (node.left == null) {
node.left = new TreeNode(null, item, null);
break;
}
node = node.left;
} else {
if (node.right == null) {
node.right = new TreeNode(null, item, null);
break;
}
node = node.right;
}
}
}
OPTION 2:
public void add(int item) {
TreeNode nodeNew = new TreeNode(null, item, null); // explicit
if (root == null) {
root = nodeNew;
return;
}
TreeNode node = root;
while (true) {
if (item < node.item) {
if (node.left == null) {
node.left = nodeNew;
break;
}
node = node.left;
} else {
if (node.right == null) {
node.right = nodeNew;
break;
}
node = node.right;
}
}
}

Option 1 is more optimizied as it involves less steps.
Option 2 means you create a variable holding a reference to only use it to assign - it only may look better for some people. In option 1 you skip the temporary reference and make the assignment and object creation directly.
At the end of the day however, there isn't another difference.
Also, in option 2 it is better to move your declaration of nodeNew below the return.
Why initialize something if you won't use it.
if (root == null) {
root = node;
return;
}
TreeNode nodeNew = new TreeNode(null, item, null); // explicit

First of all, I think the line root = node; in option 2, you probably intend it to be: root = nodeNew;, do you?
There is a potential benefit I think, in that everywhere that nodeNew is referenced in option2, it's within a conditional, so it may never be used. In this case, option1 has the advantage of only instantiating a new TreeNode object when it's needed.
In this particular case though, it looks like each call to this add method will always need to create a new TreeNode -- if not in the initial condition, then at some iteration of the while loop; so in this case there isn't really an efficiency benefit that I can see in that respect.
One aspect of this that might be worth thinking about (and talking to other people who may work on your code also), is the maintainability of it. I see a potential advantage and a potential disadvantage to option1 in this case. Maybe these situations don't apply to the specific example that you've given, but may to others that this question applies to:
Potential advantage:
If this is a long section of code, it's always clear at all of the points where a new TreeNode is being created, that it is in fact a TreeNode object that's being created (and not, say, some derivative), and what parameters were passed to its constructor.
Potential disadvantage:
If it will never be subclassed, and all instantiations in this method will be given the exact same parameters, then option2 has the benefit of having one place where a change needs to be made (change one of the parameters for example).

Related

Alternative to nested loops

I wrote these method for my program and i felt that it is hard to read as there are too many loops, is there any other alternative to this code to make it look cleaner and easier to read
public static void printRoutingTable(Map <Node, List<Edge>> adj, Node Root)
{
for (Node thisNode : adj.keySet())
{
Node currentNode = thisNode;
String nextHop;
if(currentNode.getParent() != null){
do{
if(currentNode.getParent() != Root){
currentNode = currentNode.getParent();
nextHop = currentNode.getAddr();
}
else{
nextHop = currentNode.getAddr() ;
}
}
while(currentNode.getParent() != Root);
}
else
{
nextHop = ""+currentNode.getAddr();
}
nextHop = nextHop.trim();
}
}
I've not tried, but this should be a functional and recursive version of your code.
String getNextAddr(Node node, StringBuilder sb, Node root) {
sb.add(node.getAddr());
if (node.getParent() != null && node.getParent() != root) {
return getNextAddr(node.getParent(), sb);
}
return sb.toString();
}
String nextHopList =
adj.keySet()
.stream()
.map(k -> getNextAddr(k, new StringBuilder(), Root))
.collect(Collectors.toList())
It's difficult to tell what your code is trying to achieve. At the moment it's not actually doing anything because the nextHop variable is local and nothing seems to be accumulated in the loop. I'm assuming you intend to join the strings your are generating.
there's no point passing in a map if you aren't going to use it. Better to pass a collection (or, better, Stream) of nodes.
generally the root node is the only one with a null parent. So it's likely you also don't need to pass in a reference to the root node.
if parent is optional I suggest you return Optional<Node> from getParent rather than Node.
an easy way to make the code easier to read is to break the parts into separate methods that are named after exactly what they do.
So taking these suggestions into account, something like the following:
String getRoutingTable(Stream<Node> nodes) {
return nodes
.flatMap(this::getRoutingForNode)
.map(Node::getAddr)
.collect(joining(";"));
}
private Stream<Node> getRoutingForNode(Node node) {
Stream.Builder<Node> pathToRoot = Stream.builder();
for (Node c = node; c.getParent().isPresent(); c = node.getParent().get()) {
pathToRoot.accept(c);
}
return pathToRoot.build();
}
Note that in Java 9 the getRoutingForNode will become much more readable as you will be able to dispense with the Builder:
return Stream.iterate(node,
n -> node.getParent().isPresent(),
n -> n.getParent().get());

How to write base case for this recursive function that removes children of nodes that hold odd keys?

Here's the code I have so far, I thought returning it out of the function if my condition wasn't met would work but its stack overflowing...I know that I need to establish a base case but not really how....
public void removeOddSubtrees() {
if (root == null) {
return;
}
removeOddSubtrees(root);
}
private void removeOddSubtrees(Node root) {
removeOddSubtrees(root);
if (root.key % 2 != 0) {
root.right = null;
root.left = null;
root = null;
} else {
return;
}
}
i changed my helper function to the following, and i think it may be working now:
private void removeOddSubtrees(Node root){
if(root != null){
removeOddSubtrees(root.left);
removeOddSubtrees(root.right);
if(root.key % 2 != 0){
root.right = null;
root.left = null;
root = null;
}else{
return;
}
}
}
the code that you posted in the answer does work, but there are some simplifications that can be applied to it. Here's how I'd write it:
private void removeOddSubtrees(Node root) {
if (root == null) {
return;
}
if (root.key % 2 != 0) {
root.right = null;
root.left = null;
return;
}
removeOddSubtrees(root.left);
removeOddSubtrees(root.right);
}
First, I am usually checking the exit condition at the very top and exit the method immediately. That is, my condition does a return if root == null.
Second, there is no need to do root = null if root.key % 2 != 0. it actually has no effect: it places null in the parameter that the function receives but as this parameter is not used after that in this method then no one will ever see this null. Note that also the calling code will not be affected. assignment to parameters do not propagate outside of the called method.
Finally, I think it makes more sense to call removeOddSubtrees() on root.left and root.right only if the key is even. when the key is odd you are removing the left and right subtrees from the tree so doing a recursive call on these subtrees is probably meaningless as this entire subtree will be removed from the tree shortly after. Thus, in my code I do the recursive calls only if the key is even.

Equivalent subtree

I have two trees. The tree Node is defined as
class Node{
String treeId;
String type; //Each node has type which has fixed value. For example, its color: RED, BLANK, GREEN
Set<Node> children;
String ref; //The ref is a string and allowed value are "0", "1",..."10". The value is null if it is not leaf.
};
For leaf, the children set is empty.
I am wondering whether there is some existing efficient work done how to identify equivalent substree for two given tree. The equivalent is defined as:
1) Both subtree leaves are setsets leaves of original tree.
2) Both subtrees leaves have same ref value.
3) for non-leaves node, the equivalent refers to both node have same type and equivalent children.
Thanks. It would be better if there is some Java library addressing this problem.
The input should are two tree roots while output is the Node that is root of equivalent subtree. An the the tree's height is 100~ and it has more than 500 nodes.
What i did now is that I added a new field for class Node.
class Cache{
Map<String, Set<String>> map = new LinkedHashMap<String, Set<Str>>();
}
The key of map is Node id while the value is a ref set this node of this nodeid can reach. The Cache initiated when Node is initialized.
During isEquivalent compare phase, check whether overlap exists between two root's ref set. Return false if none.
I think this can help reduce the number of comparison space.
I am not sure about 1) Both subtree leaves are leaves of original tree. requirement as it seems to conflict with how to identify equivalent substree for two given tree.. Otherwise following recursive method should be able to cover other two conditions. The haveSameOriginalTree(r1, r2) method may be implemented to satisfy the first condition that I couldn't understand. r1 and r2 are roots of two subtrees that need to be checked for equivalence.
bool areEquivalent(Node r1, Node r2)
{
if(r1.children == null && r2.children == null)
{
return (haveSameOriginalTree(r1, r2) && (r1.ref == r2.ref));
}
if((r1.children == null && r2.children != null) || (r1.children != null && r2.children == null))
{
return false;
}
// if here then both must be non-leaf nodes
if(r1.type != r2.type)
{
return false;
}
if(r1.children.getCount() != r2.children.getCount()) // not sure of correct syntax for Java Sets
{
return false;
}
for(int i=0; i<r1.children.getCount(); i++)
{
if(!areEquivalent(r1.children[i], r2.children[i])) // again please correct the syntax for Sets
{
return false;
}
}
return true;
}
Let me know what you think.
Update
Here is an iterative version of the above solution. It uses stack data structure which is allocated on the heap rather than pushed on function's call stack, so not hugely different from recursive but still better. Also, since we only hold references to Nodes (rather than copying the whole object), this shouldn't be that much of an additional memory overhead if we are already loading the original tree into memory.
bool areEquivalent(Node r1, Node r2)
{
Stack<Node> s1 = new Stack<Node>();
Stack<Node> s2 = new Stack<Node>();
Node n1, n2;
s1.Push(r1);
s2.Push(r2);
while(true) // Need a better check
{
if(s1.getCount() != s2.getCount())
{
return false;
}
if(s1.getCount() == 0) // if both stacks are empty then we've traversed both trees without failure.
{
return true;
}
n1 = s1.Pop();
n2 = s2.Pop();
if(!areEquivalentNodes(n1, n2))
{
return false;
}
foreach(Node child in n1.children)
{
s1.Push(child);
}
foreach(Node child in n2.children)
{
s2.Push(child);
}
}
}
// only checks the two nodes are equivalent. their childrens' equivalence will be handled by other calls to this method.
bool areEquivalentNodes(Node n1, Node n2)
{
if(n1.children.getCount() != n2.children.getCount())
{
return false;
}
if(n1.children.getCount() == 0) // if both are leaf nodes...
{
if(n1.ref != n2.ref)
{
return false;
}
}
else // both are non-leaf
{
if(n1.type != n2.type)
{
return false;
}
// the condition that children of non-leaf nodes be equivalent will be covered by subsequent calls this method...
}
return true;
}
Please note that both solutions expect children of two equivalent nodes in the same order. If children are not ordered then we will need to sort them before calling above code.
Let me know if this is better.

How to "delete" a node from a binary search tree in Java?

I made a binary search tree in Java but I'm having troubles whit the deleting nodes part. I managed to erase the node when it has only 1 son, and I have the idea to make the deletion when it has 2 sons, anyways the method I'm using when it has no sons (when it's a leaf) is not working in Java. Normally in C++ I would assign the Node "null" but it doesn't work here.
if (numberOfSons(node) == 0) {
node= null;
return true;
}
That's the portion of the code that takes care of the nulling part. When I debug it, it is referencing the correct node and it's assigning it the null value, but when I return to the Frame where I'm calling the delete method for my tree the node is still there. What's the correct way to "null" an object in Java? I thought everything was a pointer in here and therefore this would work, but I think it doesn't.
When you're nulling something you just make the reference in the scope you're in null. It doesn't affect anything outside.
Let me explain by example. Say you have a method foo:
public void foo(Node node) {
node = null;
if(node == null) {
System.out.println("node is null");
} else {
System.out.println("node is not null");
}
}
Now you call it like this:
public void doSomething() {
Node node = new Node();
foo(node);
if(node == null) {
System.out.println("Original node is null");
} else {
System.out.println("Original node is not null");
}
}
In your console you'll get:
node is null
original node in not null
The reason is that it's not a pointer, it's a reference. When you're nulling a reference, you just say "make this reference synonym to null". It doesn't mean that the object is deleted, it may still exist in other places. There is no way to delete objects in java. All you can do is make sure no other object points to them, and the garbage collector will delete the objects (sometime).
Nothing remains but to reinsert either left or right subtree. For instance:
class BinaryTree<T extends Comparable<T>> {
class Node {
Node left;
Node right;
T value;
}
Node root;
void delete(T soughtValue) {
root = deleteRec(root, soughtValue);
}
Node deleteRec(Node node, T soughtValue) {
if (node == null) {
return null;
}
int comparison = soughtValue.compareTo(node.value);
if (comparison < 0) {
node.left = deleteRec(node.left, soughtValue);
} else if (comparison > 0) {
node.right = deleteRec(node.right, soughtValue);
} else {
if (node.left == null) {
return node.right;
} else if (node.right == null) {
return node.left;
} else {
// Two subtrees remain, do for instance:
// Return left, with its greatest element getting
// the right subtree.
Node leftsRightmost = node.left;
while (leftsRightmost.right != null) {
leftsRightmost = leftsRightmost.right;
}
leftsRightmost.right = node.right;
return node.left;
}
}
return node;
}
}
As Java does not have aliases parameters as in C++ Node*& - a kind of in-out parameter, I use the result of deleteRec here. In java any function argument that is an object variable will never change the variable with another object instance. That was one of the language design decisions like single inheritance.

Java Pass by value, Advantage or Disadvantage?

Java is pass by value. What if I need a pass by reference. For example in the following code I need a pass by reference mechanism.
public class BinaryTree {
public TreeNode root;
public BinaryTree(){
root = null;
}
public TreeNode insert(TreeNode temp,int x){
if(temp == null){
temp = new TreeNode();
temp.key = x;
temp.left = temp.right = null;
return temp;
}
if(temp.key > x)
temp.left = insert(temp.left,x);
else if(temp.key < x)
temp.right = insert(temp.right,x);
return null;
}
}
When insert is called with root, I need root to be passed as a reference so as to change its value. But this does not happen in Java, since it's pass by value. In C/C++ the above could be easily achieved. Don't you think that this is a drawback of Java? How could such problems be solved in Java?
In Java, if you have a reference type the reference is passed by value.
Inside the method you can mutate the object that was passed and the caller will see those changes.
Don't you think that this is a drawback of Java?
No. Because:
There are few cases where you really need it.
There are workarounds (see below).
Implementing pass by reference in Java would be difficult. It makes code generation and garbage collection significantly more complicated.
(OK ... so really these are counter-arguments. But we are talking about a language design issue here, and any rational discussion of language designs has to weigh up the pros and cons of supporting a particular feature. And that includes implementation cost and performance issues.)
How could such problems be solved in Java?
The general approach is to restructure your code so that the variable you need to update in the called method is replaced with a reference to a mutable object or an array. This may entail the caller doing a bit more work, but that is generally acceptable.
Alternatively (and in your example) restructure the code so that call by reference is unnecessary.
In your example, there are two observations to make:
The "call-by-reference" mechanism is only used in the case where the tree is empty. It is not difficult to change this so that it is not necessary.
In fact, your use of call-by-reference, and in fact the entire insert method, is a leaky abstraction. There is nothing to stop you calling the method with a node object that is nothing to do with the current BinaryTree instance. You are relying on the caller to maintain the (implied) invariants of the tree.
The following version addresses both of these issues:
public class BinaryTree {
private static class TreeNode { ... }
public TreeNode root;
public BinaryTree(){
root = null;
}
public void insert(int x) {
root = insert(root, x);
}
private TreeNode insert (TreeNode node, int x) {
if (node == null) {
return new TreeNode(x);
}
if (node.key > x)
node.left = insert(node.left, x);
else if (node.key < x)
node.right = insert(node.right, x);
return node;
}
}
(I don't exactly like the way that we reassign the left / right pointers at each level after the insertion, but it does make the insertion logic simple.)
root can be changed by getting the return value.
public void insert(int x) {
root = insert(root, x);
}
I changed the method insert(...) a little.
private TreeNode insert(TreeNode temp,int x){
if(temp == null){
temp = new TreeNode();
temp.key = x;
temp.left = temp.right = null;
}
if(temp.key > x)
temp.left = insert(temp.left,x);
else if(temp.key < x)
temp.right = insert(temp.right,x);
return temp;
}
Java is pass by value for everything. Whether you are working with primitives or with reference types.
The "value" or a reference type is the reference itself, so when using reference types the reference itself is passed.

Categories

Resources