BinarySearchTree insert method - java

I am trying to use recursive to write an insert method for BST.
public void insert(DictEntry data) throws BSTException {
if (find(data.getPosition()) == data){
throw new BSTException();
}
else {
if (current == null){
root.setRoot(data);
}
else {
while(current != null){
if (data.getPosition().compareTo(root.getRoot().getPosition()) < 0){
current = current.getLeft();
}
else{
if (data.getPosition().compareTo(root.getRoot().getPosition()) > 0){
current = current.getRight();
}
else
;
}
insert(data);
}
}
}
}
But I don't know for some reason the test case always fail.
Could someone help me fix it please?

There are a few problems with this code:
You mixed up the recursive implementation with the iterative one. When using recursion as you do by calling "insert(data)" inside the function "insert" you do not need the "while" loop
When you finally hit the base case of your recursion if (current == null){ you insert at the root? You should insert at "current", as this is the sub tree you found out to be empty and match the order
You always compare your data to "root" instead of "current"
Further issues:
Your code is badly formatted ("}" after the first "else" should be indented more, "else ;" is unnecessary)
You are using recursion by updating a variable outside of the function: "current". This may work, but it is bad style. Your method should look like public void insert(DictEntry data, BSTNode node) where you start with insert(data, root) and then recursively call insert(data, node.getLeft() or insert(data, node.getRight()

Related

Java Doubly Linked list delete method

My problem is my delete method isn't deleting the node I want to delete and giving me a infinite loop.
public void delete(String name){
Node current = head;
boolean checker = false;
while(current != null && current.name != name && checker != true){
try{
if(current.name.equals(name)){
Node p = current.previous;
Node q = current.next;
/*The code can somehow get through only above this line, below here its not anymore*/
p.next = q;
q.previous = p;
System.out.println("Item successfully deleted.");
checker = true;
}else if(!current.name.equals(name) && current == tail){
System.out.println("Item not found.");
}
current = current.next;
} catch(NullPointerException e){}
}
}
Im here to asking for a hint or tip about my problem
(Sorry for my bad english)
You are checking if you have reached the end of the list current == tail but not breaking out of it. You can add a break statement inside your else if.
Other than that, you are using == to compare strings. I'm not sure why you added that there and it can be removed. Also, you must (almost always) never catch a NullPointerException.
"infinite loop" means your loop condition is incorrect, you are not making progress in each iteration, or there is a cycle your data. You use both current == null and current == tail to signify that it's the last element. Choice one way. Suggest you rewrite your loop condition to only deal with iteration, and have a conditional with a break if you have a match in the body:
for(current = head; current; current = current.next) {
if(current.name.equals(name)) {
if(current == head)
head = current.next
else
current.previous.next = current.next;
if(current == tail)
tail = current.previous;
else
current.next.previous = current.previous;
break;
}
// if tail.next is not initialized to null
// if(current == tail) break;
}
I see a potential infinite loop with no side effect here. If your list contain a node with node.name set to null then the invocation of current.name.equals(name) results in a NullPointerException. If you are at either end of the list the next or previous pointers will be null which will also result in the same exception. This exception is caught and discarded. Note that this prevents the advance of the current pointer which causes the same iteration to occur. At the very least make sure to print out the exception even if you're not taking any other action. It'll help with debugging.
Your while loop condition is overly complicated. while(current != null) should suffice given that:
Using if(current.name.equals(name)) removes the need for current.name != name. Also, don't use == or != for string comparison. It is a pointer comparison. Most equals methods take care of pointer comparisons.
Use a break or return for flow control here and remove checker boolean. The tail.next should always point to null to signify the end of the list. The only reason I see to have the checker boolean is if delete should remove all matching nodes and you want to know if it happened at least once. From what I see in the code that is not the case.
I would rewrite this as:
public void delete(String name){
Node current = head;
while(current != null){
try{
if(current.name.equals(name)){
...
return;
// Do not advance current here. Refer to finally block below.
}
} catch(NullPointerException e){
e.printStackTrace();
return; // If function should stop on error.
} finally {current = current.next;} // This prevents the repeat as it always happens.
}
System.out.println("Item not found.");
}
Note if you use "break" instead of "return" then the "Item not found." line will always print. You'd have to guard it with an if statement and a flag.
public void delete(String name){
Node current = head;
while(current != null){
if(current.name.equals(name)){
if(current.prev != null){
current.prev.next = current.next
}
if(current.next != null){
current.next.prev = current.prev
}
System.out.println("Removed node")
break;
}
current = current.next;
}
}
You could use this logic to delete the node that matches the name(given name is always present) if node is non null.

Java is asking to return a value even when the value is returned in the if-else ladder

I was trying to code AVL tree in java and something has just shaken my understanding of basic programming. Why does Java force me to return a value when I already have a return statement in the else block of the if-else ladder. I tried debugging and it works as expected, it never goes to the returns statement outside the if-else blocks. I have been working on java and never realized this. I am not even sure if this was always there or is it something new? I also read a few blogs which say that you don't have to return a value if you have returned it from the else block.
The error which I get is I skip the last return statement.
Exception in thread "main" java.lang.Error: Unresolved compilation problem:
This method must return a result of type AVLNode
https://javahungry.blogspot.com/2019/12/missing-return-statement-in-java.html
checkout the last example on this link.
public class AVL
{
AVLNode root;
private AVLNode insert ( AVLNode current,int val)
{
if ( current == null)
{
return new AVLNode(val, 0, null, null);
}
else if ( val < current.val)
{
current.left = insert ( current.left, val);
}
else if ( val > current.val)
{
current.right = insert ( current.right, val);
}
else
{
return current;
}
return current; // I don't want to return you ////
}
public void add ( int val)
{
this.root = insert ( this.root, val);
}
The reason you get this error is because if you didn't have the final return statement, and ended up inside one of the if blocks, you'd never hit the else and therefore never hit a return statement.
Try stepping through the code with any case where current.val and val are both specified, but are two different values, for example current.val = 2 and val = 1.
In this case the execution will skip over the first if (because current is not null), and then go into the second if block (because val > current.val). Because one of the if conditions has been met, we will skip over the following else if and the else, and hit the final return statement. If that statement weren't there, we'd never return anything.
The reason this is different from the example you cite is because that example has a return in both the if and the else blocks, so whatever happens you always encounter a return statement. You could achieve the same by adding return current; inside both else if blocks, although it's far cleaner just to have the single return at the end.
Basically, if you have a collection of if...else if...else if...else the execution will only ever visit one of the blocks. Whichever path through the code gets executed must always have a return.
you either use
else
{
return current;
}
or
return current;
so in my opinion your "if-else" is a conditional way to set the correct value for the variable "current" so using "else" for returning purposes is kind of strange,so you code can be refactored as this: use the if-else to catch all possible cases in the condition, set the variable in ever case and then, when you are done, return the value, like:
private AVLNode insert ( AVLNode current,int val)
{
if ( current == null)
{
return new AVLNode(val, 0, null, null);
}
else if ( val < current.val)
{
current.left = insert ( current.left, val);
}
else if ( val > current.val)
{
current.right = insert ( current.right, val);
}
return current;
}
One of the principle of Clean code is method should have only one return statement so even if your code is good and working you could do it would be better if you do something like this
private AVLNode insert ( AVLNode current,int val)
{
AVLNode returningObject = current;
if ( current == null) {
current = new AVLNode(val, 0, null, null);
} else if ( val < current.val) {
current.left = insert ( current.left, val);
} else if ( val > current.val) {
current.right = insert ( current.right, val);
}
return returningObject ;
}
And that solve the issue with // I don't want to return you ////
But you problem appears cause Java is smart enough to check all your statements before you run your code, and if you put all your returns into block statements Java knows that there is situation where your statements can be not achieved and force you to always return something from the method.
This is the simplest way I can explain it.
All of your recursive calls to insert () sooner or later end up on one of the branches which contain a return (or let's assume that for the sake of the discussion).
I think that your misconception is that this is enough.
But each level of recursive call has to get its own return.
So these two lines
current.left = insert ( current.left, val);
/* ... */
current.right = insert ( current.right, val);
each also need a return, in order to return from the level which did the calling, the implied return at the deepest nested recursive call level is only for that level.
And not just for Java, either. When you have a nested structure like the one in the OP, when it matches the first 'if' and starts down your nested if-then-else-if ladder, then it is never going to somehow fall through to the 'else' that matches the top-level 'if' statement because it has already evaluated that condition and chosen its execution path.
The conditions you are testing for are independent and unconnected, so there is no need for the else construct at all. Once we have dealt with the null case and returned a new AVLNode, they are just simple unconnected if statements that conditionally modify the state of current.
private AVLNode insert ( AVLNode current, int val) {
if (current == null) {
return new AVLNode(val, 0, null, null);
}
if (val < current.val) {
current.left = insert(current.left, val);
}
if (val > current.val) {
current.right = insert(current.right, val);
}
// nothing changes when val == current.val, is this correct?
return current;
}

Unable to understand when a return statement must be used in association with recursion

I was brushing up on the concept of recursion and working with binary trees. I don‘t understand the difference between instances of using a return statement with a recursive call and other times when return is not used. I‘ll give code illustrating my question.
Here is a function that checks if a value is present in a BST:
public boolean containsNodeRecursive(Node current, int value) {
if (current == null) {
return false;
}
if (value == current.data) {
return true;
}
//HERE, THE RECURSIVE CALL IS PRECEDED BY A RETURN STATEMENT
return value < current.data
? containsNodeRecursive(current.left, value)
: containsNodeRecursive(current.right, value);
}
}
Here is a function for insertion of data into a BST:
public Node insert(Node current, int data) {
if (current == null) {
return createNode(data);
} else if (data < current.data) {
//RECURSIVE CALL HERE WITHOUT USE OF A RETURN STATEMENT
current.left = insert(current.left, data);
} else if (data > current.data) {
current.right = insert(current.right, data);
}
return current;
}
I think my question boils down to: “When should we return a recursive call and when should we not?”
There are two types of functions presented, so there are two separate rules we need to follow.
Take the first function you provided:
public boolean containsNodeRecursive(Node current, int value) {
if (current == null) {
return false;
}
if (value == current.data) {
return true;
}
//HERE, THE RECURSIVE CALL IS PRECEDED BY A RETURN STATEMENT
return value < current.data
? containsNodeRecursive(current.left, value)
: containsNodeRecursive(current.right, value);
}
}
This function is intended to find a single target value and return it. In this case, we return a recursive call when we want to return a value that we know will be provided by a single child of the node being tested.
However, take the insertion example:
public Node insert(Node current, int data) {
if (current == null) {
return createNode(data);
} else if (data < current.data) {
//RECURSIVE CALL HERE WITHOUT USE OF A RETURN STATEMENT
current.left = insert(current.left, data);
} else if (data > current.data) {
current.right = insert(current.right, data);
}
return current;
}
This code is intended to accomplish a different type of task. Rather than seeking a single target value, this recursive function is intended to modify the existing tree. In this case, we return only the modifications that we want to make to the existing tree (or the duplicate value).
Since Java doesn't do tail-recursion optimization, you never have to "return a recursive call". See "Why doesn't Java have optimization for tail-recursion at all?"
See "What is tail recursion?" if you want to learn about how that works in functional languages, but it isn't applicable to Java.
In Java, you do the recursive call wherever in the method it'll make more sense to you. You don't have to do it as part of a return statement.
The return keyword preceding the recursion is not different from this:
boolean res = value < current.data
? containsNodeRecursive(current.left, value)
: containsNodeRecursive(current.right, value);
return res;
Either way the recursion is fully evaluated before the current function-call can return.
“When should we return a recursive call and when should we not?” - you're not returning a recursive-call, you're returning the result of the recursion, e.g a boolean. You can return the result of a function-call immediately if you do not have any more processing to do. This obviously requires the function-call to return the same type as the current function is expected to return, which is guaranteed for recursions as it calls "itself".
For your first code-snippet, consider what the function does. It searches for a value, and if found further down the tree, then that boolean value is simply forwarded back up the tree.
In your second example, the tree is being changed as part of the recursion. For any node current, the recursion returns the left/right subtree, which must then overwrite the existing left/right subtree before being able to return.

Infinite recursion in Java with splay trees

I am trying to write a "contains" method for a splay tree to figure out if a node is already in the tree. I give this method a node to start searching and a string key to use find the corresponding node. I think I have a pretty good handle on recursion, but I am stumped by this. I've bolded the two lines that are causing the infinite recursion, but I'm stuck because, unless you somehow have a tree with an infinite number of elements, wouldn't the left and/or right elements have to be null at some point? They cannot be != to null forever! I might be losing my mind but I would very much appreciate any clarification on how to create a stronger base case.
tldr: how is it possible for this function to recurse infinitely when we have to run into null at some point?!
public BST_Node containsNode(BST_Node node, String s) {
BST_Node result = null;
if (node == null) {
return null;
}
if (node.data.compareTo(s) == 0) {
splay(node);
return node;
}
if (node.left != null) {
result = containsNode(node.left, s); //recursion here
}
if (result == null && node.right != null) {
result = right.containsNode(node.right, s); //recursion here
}
return result;
}
}

Why is this generating a StackOverflowError

I am a beginner with trees and I was just trying to implement one for the first time and am generating the stackoverfloweror. I know its probably related to a bad recursion call however I dont see anything wrong with the recursion could I get a little help? The error is in this code.
public void insert(Node node, String value)
{
if((value.compareTo(node.getValue())) < 0)
{
if (node.left != null)
{
insert(node.left, value);
}
else
node.left = new Node(value);
}
else if((value.compareTo(node.getValue())) > 0)
{
if(node.right !=null)
{
insert(node.right, value);
}
else
node.right= new Node(value);
}
}
I call that method here
public static void main(String[] args) throws FileNotFoundException, IOException {
Tree dataTree = new Tree(new Node("m"));
File file = new File("C:\\randomdata.csv");
BufferedReader br = new BufferedReader(new FileReader(file));
String line;
while((line = br.readLine()) != null){
if(line.toString().contains("zogeti"))
break;
else
{
dataTree.insert(dataTree.getRootElement(),line);
}
}
br.close();
}
If the file is initially sorted, then this function looks like it will recurse N times for
a file with N lines. Java doesn't implement tail recursion, so this is sure to be a real
recursion. Rewrite it as a while loop instead of as a recursive function.
This would most likely occur if node.left == node or node.right == node or some other longer cycle in your tree.
In its current form, if the value was equal, it wouldn't trigger either if block, and would simply return (and return up the trace, as well) not adding anything. This means the cycle is probably occuring outside of this method.
You might find this bug in the only other place you're likely to create elements outside of the insert nethod: your constructor of your Tree class.
How big is this CSV file? Larger the file, deeper the recursion will be, leading to stackoverflow.
Try executing java with following command line parameters.
-Xms512m -Xmx512m
Also, what if the new line read from file is same as existing node value?
Following code will ignore that... (might be a requirement, I am just saying).
if((value.compareTo(node.getValue())) < 0)
{
if (node.left != null)
{
insert(node.left, value);
}
else
node.left = new Node(value);
}
else if((value.compareTo(node.getValue())) > 0)
{
if(node.right !=null)
{
insert(node.right, value);
}
else
node.right= new Node(value);
}
If your file is 3260953 lines long and sorted that would certainly explain your problem. If the elements are in ascending sorted order, then every time insert inserts a new element, it gets placed on the right branch of each node every time. What you end up with is a string of 3260953 linearly linked nodes which your code accesses through as many recursive calls. This will overflow the stack. Try running on a MUCH smaller file and in scrambled alphabetical order.
Red-Black Trees avoid this issue by automatically balancing the tree by redistributing the elements. Coding up such a data structure isn't so straight-forward, however.

Categories