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.
Related
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;
}
}
i am trying to find a path in between two nodes(sourcenode and targetnode). i came up with this code but i cant seem to make it recursively find a path. i even set the nodes to null if the target node is found but i keep getting a stack overflow error.
public void findPathBetween(Node sourceNode, Node targetNode){
//find a path between the sourceNode and targetNode
//select the nodes and edges along the path if one exists.
ArrayList<Node> nodesToSearch = new ArrayList<Node>();
nodesToSearch.add(sourceNode);
//basis
if(sourceNode == null || targetNode == null) return;
//recursion
ArrayList<Node> newNodesToSearch = new ArrayList<Node>(); //get nodes for next level to search
for(Node aNode : nodesToSearch) {
for (Node neighbour : aNode.getNeighbours()) {
if (neighbour != targetNode && newNodesToSearch.isEmpty()) {
newNodesToSearch.add(neighbour);
neighbour.setSelected(true);
edgeBetween(aNode, neighbour).setSelected(true);
sourceNode = neighbour;
}
if (neighbour == targetNode) {
sourceNode = null;
targetNode = null;
return;
}
}
}
if(sourceNode != null &&targetNode != null) {
findPathBetween(sourceNode, targetNode);
}
}
You are storing the state inside the recursion function, and don't pass it to the next iteration. Thus you are simply running one function over and over without changing its arguments and the state that affects its execution.
I do not want to correct your code because I would have to guess you intents in order to provide a good explanation.
Anyway I think that you were trying to implement DFS, so I suggest you to take a look at some working implementations. Just google them, there are plenty, e.g. this one comes with a piece of theory, is simple, and was written by Robert Sedgewick, so it can be trusted.
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()
I have developed a code to merge two already sorted linked lists in java.
I need help with the following:
How do I retain the value of head node of merged list without using tempNode?
Can this code be better optimized?
public static ListNode mergeSortedListIteration(ListNode nodeA, ListNode nodeB) {
ListNode mergedNode ;
ListNode tempNode ;
if (nodeA == null) {
return nodeB;
}
if (nodeB == null) {
return nodeA;
}
if ( nodeA.getValue() < nodeB.getValue())
{
mergedNode = nodeA;
nodeA = nodeA.getNext();
}
else
{
mergedNode = nodeB;
nodeB = nodeB.getNext();
}
tempNode = mergedNode;
while (nodeA != null && nodeB != null)
{
if ( nodeA.getValue() < nodeB.getValue())
{
mergedNode.setNext(nodeA);
nodeA = nodeA.getNext();
}
else
{
mergedNode.setNext(nodeB);
nodeB = nodeB.getNext();
}
mergedNode = mergedNode.getNext();
}
if (nodeA != null)
{
mergedNode.setNext(nodeA);
}
if (nodeB != null)
{
mergedNode.setNext(nodeB);
}
return tempNode;
}
1: You have to keep a record of the first node, which means you will have to store it in a variable such as tempNode.
2: No. There's not much to optimize here. The process is quite trivial.
There are a few possibilities:
1) Instead of using mergedNode to keep track of the previous node, use nodeA.getNext().getValue() and nodeB.getNext().getValue(). Your algorithm will become more complex and you will have to deal with a few edge cases, but it is possible to eliminate one of your variables.
2) Use a doubly linked-list, and then use either nodeA.getPrev().getValue() and nodeB.getPrev().getValue() instead of mergedNode. You will also have to deal with edge cases here too.
In order to deal with edge cases, you will have to guarantee that your references can not possibly be null before calling getPrev(), getNext() or getValue(), or else you will throw an exception.
Note that the above modifications sacrifice execution time slightly and (more importantly) simplicity in order to eliminate a variable. Any gains would be marginal, and developer time is far more important than shaving a microsecond or two off of your operation.
I need to make a method that removes the last element of a LinkedList using recursion.
This is what I have so far but it doesn't seem to be removing the node...when i call list.size() it is still the same size with the same values. What am I doing wrong here?
This is for Java by the way
public void removeLastElement(Node curr){
if (curr == null)
return;
else{
if(curr.next == null)
curr = null;
else
removeLastElement(curr.next);
}
}
In a LinkedList to remove the last element you have to get the penultimate element and set
curr.next = null
You're in the right way to get the recurrent function to remove the last node. The problem is you're identifying the penultimate node with curr.next == null, if you got it, you nullify it, but that's your actual input! So, you must check if the actual node is the antepenultimate node on the list:
if (curr.next.next == null) {
curr.next = null; //Now you're modifying the data in your input.
}
With this change, there are more basic cases to check, but that's up to you, my friend.
Boolean deleteLast(Node n)
{
if(n.next == null)
return true;
if(deleteLast(n.next))
{
n.next = null;
return false;
}
return false;
}
Node deleteLast(Node n) {
if (n.next == null)
return null;
n.next = deleteLast(n.next);
return this;
}
The general idea is you ask the next node "hey, can you tell me where you are, and delete your last node?" The last node can then just say "I'm nowhere" and it'll all fall into place.
This is very similar to Aadi's answer, just using Nodes instead of booleans.