Stackoverflow error when recursively searching in a tree - java

I am implementing the Shannon/Fano algorithm using Java and I am doing this by calculating the frequencies of symbols in a text file, and after that I put all these values in a tree. The problem is that when I am searching for a certain symbol in a tree I also have to update the code of the respective symbol (e.g If I go to left append 0, otherwise 1) and doing this recursively I am getting a stackoverflow error. Below is my code :
private String getNodeValue(Node node, String symbol) {
if (node.getLeftChild() != null) {
if (node.getLeftChild().getData().equalsIgnoreCase(symbol)) {
node.updateCode(0);
return node.getData() + "";
}
} else if (node.getRightChild() != null) {
if (node.getRightChild().getData().equalsIgnoreCase(symbol)) {
node.updateCode(1);
return node.getData() + "";
}
}
Node nextLeftNode = node.getLeftChild().getLeftChild();
if (nextLeftNode != null) {
getNodeValue(nextLeftNode, symbol);
}
Node nextRightNode = node.getRightChild().getRightChild();
if (nextRightNode != null) {
getNodeValue(nextRightNode, symbol);
}
// if symbol is not found return null
return null;
}
and the stackoverflow error is triggered at the very first line of the method when the call to node.getData() takes place. This is my stack trace:
Exception in thread "main" java.lang.StackOverflowError
at ro.uvt.it.datastractures.Node.getData(Node.java:47)
at ro.uvt.it.datastractures.Node.getData(Node.java:47)
at ro.uvt.it.datastractures.Node.getData(Node.java:47)
And this is my getData() method:
public String getData() {
return this.getData();
}
Any help or hint would be appreciated,
Thank you.

There are many mistakes in your code.
As you showed in your stacktrace, the infinite recursion is in the getData method and not in the getNodeValue method, so you need to post the source code of the getData method.
But the getNodeValue method has many bugs as well.
Your first two if statements have exactly the same condition:
if (node.getData().equalsIgnoreCase(symbol)) {
and
else if (((String) node.getData()).equalsIgnoreCase(symbol)) {
the returns inside these if statements append an empty string to the result of getData(), which already returns String. Replace each of them with:
return node.getData();
are just a different way of writing the same, since getData() already returns a String so casting to String again doesn't make a difference.
Your next to if statements recursively call getNodeValue on leftChild and rightChild, but they never return anything, so you always end up returning null in your code once you're past the first two identical if statements in your method.
You code should probably read:
if (node.getLeftChild() != null) {
String found = getNodeValue(node.getLeftChild(), symbol);
if (found != null) {
return found;
}
}
if (node.getRightChild() != null) {
String found = getNodeValue(node.getRightChild(), symbol);
if (found != null) {
return found;
}
}

Almost certainly unbounded recursion. At a guess I'd say it was a mistake in the implementation of getLeftChild or getRightChild. I would suggest you step through in the debugger, and I'll bet you will quickly see where this goes.
However, if you have a very deep tree, you may discover that the stack overflow exception is legitimate, in which case you will need to revisit the algorithm. And the traditional technique of tail recursion appears to be hard to achieve in Java.

Related

Handle null pointer exception

I have the following code:
String a= null
Element element = ...
if(element == null) {
System.out.println("...");
}
a = element.getText();
I got a null pointer exception on this code.
I thought that it would be better to use an if else statement in order to avoid this error.
String a= null
Element element = ...
if(element == null) {
System.out.println("...");
} else {
a = element.getText();
}
Is it a good solution to use the above code to solve the problem or it would be better to manage it another way?
I got a null pointer exception on this code
Yes, because you don't not execute element.getText() if element == null.
Element element = ...
if(element == null) { // This check is irrelevant to...
System.out.println("...");
}
a = element.getText(); // ...whether this statement is executed.
Is it a good solution
It's a solution, because the else isn't executed when element == null.
You might consider inverting the condition, so the "happy" case (the one you want to execute when things are working normally) comes first. But this is not an important difference, functionally.
if(element != null) {
a = element.getText();
} else {
System.out.println("...");
}
You approach is ok since it works and you dont get the NPE anymore.
You could invert the if condition and simplify things like:
String a= null
Element element = ...
if(element != null) {
a = element.getText();
}
This way you do your things in a protected code block and you don't need else part unless you really wanted to print "..."
Yes it is a solution for the reasons already mentioned by the other answers. I want to provide another solution:
String a;
try {
Element element = ...;
a = element.getText();
} catch (NullPointerException e) {
System.out.println("...");
a = null; // Or some other value that can be tested but cannot produce a NPE
}
This would have the same error handling as your if statement, but it has the advantage, that ´element´ cannot be referenced later and produce a NPE somewhere else. This is of course only useful if the String cannot produce a NPE either way and if the Element could actually used later (in other words if the method doesn't end directly afterwards).

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;
}
}

BinarySearchTree insert method

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()

How can I ensure that a method returns a value?

The method I have written below is supposed to act on a BinaryTreeNode to flatten the tree 'beneath' that node. My if-else if-else together with a recursive (?) structure, to my understanding, will always return a value, but I am getting an error in Eclipse saying "This method must return a result of type <Integer>.
After doing research, I believe that Java cannot quite tell that the method will always return a correct value, since the return statement is in an 'else'. Is this the problem? How can I alternatively design the method to avoid this problem?
//doFlatten acts on a BinaryTreeNode and takes a storage list as an argument
//it will call itself on any children on the node
//if the node is a leaf, it will update the storage list and return that updated list
public List<Integer> doFlatten(List<Integer> result){
if (this.leftChild != null){
this.leftChild.doFlatten(result);
}
else if (this.rightChild != null){
this.rightChild.doFlatten(result);
}
else{
result.add(this.value); //add leaf to result list
return result;
}
}
Make the return type void and remove return result. There's no need to return the result object, since it's the same result object that was passed in (the caller already has a reference to it).
The best bet is to define the a variable with the return type of the method and initialize it a default value, assign the proper value of the result in the method, and in the end, as the last line of the method, return this variable.
For your method, it may look like this:
public List<Integer> doFlatten(List<Integer> result) {
List<Integer> realResult = new ArrayList<>(result);
if (this.leftChild != null){
this.leftChild.doFlatten(result);
}
else if (this.rightChild != null){
this.rightChild.doFlatten(result);
}
else{
result.add(this.value); //add leaf to result list
realResult.add(this.value);
//avoid return statement here
//return result;
}
//single point for return statement
return realResult;
}
But, for this case, as you can see in code above, it seems meaningless to even return a result because the proper result of this method is stored in List<Integer> result. So, just make your method void:
public void doFlatten(List<Integer> result) {
//rest of your code...
}
Your method actually doesn't always return a value (the first if and first else) don't have a return.
This seems to be what you want:
public List<Integer> doFlatten(List<Integer> result){
if (this.leftChild != null){
this.leftChild.doFlatten(result);
}
else if (this.rightChild != null){
this.rightChild.doFlatten(result);
}
else{
result.add(this.value); //add leaf to result list
}
return result;
}
Examine your code again. You have only one return statement in the last else branch. This means that your method returns value only if it arrives to this point. This is exactly what compiler reports you.
So, the question "how to ensure that my method returns value" can be answered like "compile your code". If you managed to compile code be sure that your method indeed returns value or throws exception.
If however you are actually asking about the best coding practices that help you to avoid such compilation errors IMHO there is no one 100% correct answer.
See the recommendations of Luigi Mendoza. In some cases they are good. However (sorry, Luigi) I cannot agree that they always good. I'd say that you should avoid if/else structures when it is possible. For example series of if statements with return in the end of if block in some cases more readable.
I think you just want :
//doFlatten acts on a BinaryTreeNode and takes a storage list as an argument
//it will call itself on any children on the node
//if the node is a leaf, it will update the storage list and return that updated list
public List<Integer> doFlatten(List<Integer> result){
if (this.leftChild != null){
return this.leftChild.doFlatten(result);
}
else if (this.rightChild != null){
return this.rightChild.doFlatten(result);
}
else{
result.add(this.value); //add leaf to result list
return result;
}
}
Notice that in my version, all predicate outcomes lead to a List<Integer> being returned, where in your case, only the else clause does.

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