Java Binary Search Tree - Recursive Void Copy Method - java

I need to create a recursive copy method for a binary search tree from scratch for an assignment of mine. The method should copy each item in a given BinarySearchTree object to the calling BinarySearchTree object. Only problem is that the method must be a void and everything I've looked up on this subject seems to utilize different return types to get this done.
I don't really know how to even start with something like this, all I have is the prettymuch empty shell of the method and it's wrapper. I'm not sure if the parameters in the private method are correct but it was my best guess as a start.
public void copy(BinarySearchTree<E> bst2){
copy(bst2, root, bst2.root);
}
private void copy(BinarySearchTree<E> bst2, Node node1, Node node2){
}
I'd appreciate any and all help.
Thanks!

Immediate thought is (rough pseudocode ahead):
class Tree {
//stuff in the tree with a root node, etc...
copyTree(Node parentTreeNode, Node copyTreeNode) {
if(copyTreeNode == null)
return;
parentTreeNode = clone(copyTreeNode) //clone just copies the node's values into the node.
if(copyTreeNode.leftChild != null) {
parentTreeNode.leftChild = new Node();
copyTree(parentTreeNode.leftChild, copyTreeNode.leftChild);
}
if(copyTreeNode.rightChild != null) {
parentTreeNode.rightChild = new Node();
copyTree(parentTreeNode.rightChild, copyTreeNode.rightChild);
}
}
}
And you would just call this with the two root nodes and let it recurse and it builds the tree for you.
So, if the base case gets triggered (the current node is null) then you skip that node by returning and move on in the recursion process (the base case is important in recursive techniques, otherwise you get infinite recursion). So, we start with the root node, copy it (if it's not null), and then move to the left subtree, assuming it's also not null. We pause in this method, call the method on the subtree, which "pauses" there and causes on the left.... When it returns back all the way down, each place it had "paused" resumes, moving to the right child on each of those nodes with the same procedure. Once the left subtree recurses on itself, we resume in the top node and move to the right subtree to do the same thing there (just like it did in all the children nodes, recursively). When it's all done, it just returns normally.
Recursion isn't particularly difficult, but it does take some practice to understand at first.
This is a rough idea and hasn't been tested, but that's roughly how I'd do it. It's probably worth noting that this style of handling it won't guarantee that the trees are the same if the main tree already had data in it, or you handed a node other than the root node. But it would be the same from below that node.

Related

How to understand a recursive inorder traversal of a BST in Java?

I am trying to understand how the well-established methods of implementing an inorder traversal of a binary search tree are functioning in Java.
I am given the following code:
public void inorder() {
if (!this.isEmpty()) {
this.getLeft().inorder();
System.out.print(this.getValue());
this.getRight().inorder();
}
}
with isEmpty() returning whether the current Node is null, getValue() returning the value of the current node, and getLeft() as well as getRight() respectively returning the left and right successor node.
My problem here is, I do not understand how one is able to process the traversal with this code. I have visualized my chain of thoughts on a sheet of paper for you guys to see, with the circles being the nodes, the black squares being null nodes, and the encircled node being the current (this) object. As I am following the pseudocode, I would land in a null node at the end and hit the recursive dead-end case. I also do not at all understand how the code is able to go back in the tree hierarchy once we've already set subtree nodes as the current this object.
My visualization
Am I imagining this wrong, and if so, could someone help me understand the right way of doing this? The code works, but I really need to understand how that comes through. Any help would be super super appreciated
As I am following the pseudocode, I would land in a null node at the end and hit the recursive dead-end case
When you reach the "dead-end" case, this means the current branch of the recursion tree ends. It doesn't mean the entire recursion ends.
After all, when method X calls method Y, and method Y ends, the control returns to method X. This remains true when methods X and Y have the same name. The recursion only ends after the original inorder() call (which was executed on the root of the tree) returns.
You can number each call to the inorder() method in order to tell them apart:
1. root.inorder() calls
2. root.getLeft().inorder() calls
3. root.getLeft().getLeft().inorder() calls
4. root.getLeft().getLeft().getLeft().inorder()
this node is empty, so inorder() #4 method returns control to the previous one (#3)
now #3 call prints the current node via System.out.print(this.getValue())
which prints the left most node of the tree
then #3 calls
5. root.getLeft().getLeft().getRight().inorder()
this node is also empty, so the current inorder() method returns control to #3
now #3 also ends, and returns control to the previous method (#2)
and so on...

Java Recursion - Alternative to passing-by-reference:

I'm migrating from C to Java and I'm having difficulties with recursion, specially because in Java you can't pass an argument by reference.
What I'm looking is not a solution/trick to force Java pass an argument by reference, but the recommended way to solve such a problem in Java.
Let's take the recursive node insertion in a binary tree:
void nodeInsert(Node n, int a) {
if (n == null)
n = new Node(a);
...
}
In C, by the end of the execution, the node n in the tree would point to the newly created node. In Java, however, n will still be null (because n is passed by value).
What is the suggested Java approach for such problems?
Some approaches I already tried:
Using a static object to keep track of the parent (issue complicates when using generics).
Passing the parent node as part of the function. It works but complicates the code a bit and doesn't look as a good solution.
Creating an additional member pointing to the parent node but this is not a good solution, as it increases the space required by O(n);
Any advice is welcome.
In Java instead of using reference variables, we use return values and assign it to the variable that has to be changed.
Node nodeInsert(Node n, int a) {
if (n == null){
n = new Node(a);
return n;
}
else
{
....
return nodeInsert(n,a); //this is how a recursion is done.
....
}
}
If you need more on recursion http://www.toves.org/books/java/ch18-recurex/ will teach you right.
A common way to implement is to maintain the node relationships inside the node itself. Quite a lot of examples can be found in implementations of various JDK datastructures. So the Node is the container for the value and contains references to other nodes, depending on the data structure.
If you need a child->parent relationship between nodes, the Node class would look like
class Node<T> {
T value;
Node parent;
}
In case of insert, you create a new node, set the parent reference to the original one, and return the new Node as a result (this is optional, but not uncommon to do, so the call has a handle of the new child)
Node<T> insert(Node<T> parent, T value) {
Node<T> child = new Node<>();
child.value = value;
child.parent = parent;
return child;
}
And yes, this adds a minor overhead of 4 bytes per Node (or 8 bytes, on 64bit JVMs without compressed pointers)
I propose the following solutions:
Implement a method in class Node that adds a child node. This makes use of the OO-possibility to encapsulate data and functionality together in a class.
Change nodeInsert to return the new node and add it to the parent in the caller (also mentioned in comments). The responsibility of nodeInsert is to create the node. This is a clear responsibility and the method signature shows what the result of the method is. If the creation is not more than new Node() it might not be worth to have a separate method for it.
You can pass a holder object that in turn references your new Node object
void nodeInsert(AtomicReference<Node> r, int a) {
if (r.get() == null)
r.set(new Node(a));
...
}
Or you could pass an array with space for one element.
After months posting this question, I realized yet another solution that is, in fact, already contemplated in java design patterns but not mentioned here: Null Object Pattern.
The downside is that each null occupies memory (in some cases, like large Red-Black trees, this could become significant).

Build Binary Tree from Preorder Traversal: Stack Overflow Error

I have a tree where the leaves are marked with L and the non-leaf nodes are marked with I. I am given the preorder traversal of the tree. An example is IIILLILILLIIILLLIILILLL. I have to build the huffman tree for this included string. I originally pass in a new Root(), 0, and my treeString for my arguments. TreeString would be the string with the I's and L's pasted above. For some reason my code causes a StackOverflow exception to be thrown. My code is as follows for the makeTree method:
public static void makeTree (BinaryNodeInterface<Character> root, int start, String treeString)
{
if (treeString.charAt(start)=='L'){
root.setLeftChild(null);
root.setRightChild(null);
return;
}
BinaryNodeInterface<Character> leftSide = new BinaryNode<Character>();
root.setLeftChild(leftSide);
makeTree(root.getLeftChild(), start++, treeString);
BinaryNodeInterface<Character> rightSide = new BinaryNode<Character>();
root.setRightChild(rightSide);
makeTree(root.getRightChild(), start++, treeString);
}
I have no idea what is causing the stackoverflow exception to be thrown. I would think that my base case at the beginning would return and handle it.
I believe this is the problem:
makeTree(root.getLeftChild(), start++, treeString);
I'm not sure what your approach is, but it looks like if you see an I, your plan is to go to the left node, and start examining the string starting at the next character.
The reason for the infinite recursion is that start++ is a post-increment operator, which means that it gives you the current value of start and then increments it. Thus, each time makeTree calls itself, it calls itself with the same version of start, and thus looks at the same I in the input string.
However, changing it to ++start will not make things work. (It might avoid the stack overflow, but it won't work right.) The reason is that when you call makeTree, you want to give it the starting location of the string where you want the recursive call to start looking--but you also want the recursive call to tell it how much of the string it consumed. That is necessary because, after makeTree calls itself recursively on getLeftChild, you will call it again on getRightChild, and you need to call it with the correct starting point.
Note that each recursive invocation has its own copy of start. Thus, when makeTree calls itself, and the second makeTree increments start, this has no effect on the start that the first makeTree sees.
You'll somehow need each recursive makeTree to tell its caller how much of the string it consumed. Probably, the simplest way to do this is to change the return type to int; you can decide whether you want the function result to be the number of characters consumed, or the index where it stopped scanning, or something similar. Then, after calling makeTree recursively, use the function result to adjust the start parameter. Make sure makeTree returns the correct result, both in the leaf and the non-leaf cases. Be careful to avoid off-by-one errors.
You cannot create a tree just from preOrder list.
Atleast you need inorder traversal as well OR if is full tree then you can also use posrorder.

Java: "The JAR file has no attachment" error, but only when I override the toString() method

I have written a class, called Node, which represents nodes in a graph. It looks like this:
public class Node{
protected ArrayList<Node> neighbours = new ArrayList<>();
public Node(ArrayList<Node> neighbours){
for(int i=0;i<neighbours.size();i++){
this.neighbours.add(neighbours.get(i));
}
}
public Node(){}
public void setNeighbours(ArrayList<Node> neighbours){
this.neighbours.clear();
this.neighbours.addAll(neighbours);
}
public ArrayList<Node> getNeighbours(){
return this.neighbours;
}
#Override
public String toString(){
String s = new String(""+this.neighbours);
return s;
}
}
I tested it before overriding the toString method, creating a basic graph. The output was correct, the only problem was that the output was the address of each object, instead of the object itself( like Node#61672c01 for example). After writing thetoString method, I started getting tons of errors like "the source file does not have attachment" and java.lang.StackOverflowError errors
I tried changing the build path of the project ( I thought that wrong build type is the reason ), but this didn't help. I thought that there is something wrong with the recursion, because of java.lang.StackOverFlowError, but there weren't any problems before I wrote the toString() method.
You are in recursive loop that never ends.
String s = new String(""+this.neighbours);
That goes to your list to print toString on each node.
Then into toString() again coming to list to print.
As someone noted before, the "source file does not have attachment" error is the way Eclipse has to tell you that you have not told it where to find the .java file with which present you a debug view of your code when failing.
The StackOverflowError happens most probably when trying to call your toString override, which calls itself the toString override of every neighbour in the list. If there is any circular link between neighbours, this will lead surely to an overflow.
I propose you discard the idea of presenting a String with all the neighbours of a Node, which leads you to a recursive problem. Instead, if you must print something, try to write a transversal walk of the graph discarding visited nodes (or try to paint something with a 2D drawing API, but that would be another completely different problem)
I can explain the cause of the StackOverflowException
You are printing neighbours not children. If each neighbour has a reference to the other neighbour then you will constantly traverse backwards and forwards between those nodes.
Node n1;
Node n2;
n1->toString scans it's neighbours which includes n2.
n2->toString scans it's neighbours which includes n1.
n1->toString scans it's neighbours which includes n2.
etc until the stack overflow happens.
Normally nodes contain references to children, not neighbours. This code would work fine if you were scanning the children of the node at each step.
Incidentally this line is redundant:
String s = new String(""+this.neighbours);
All you need is:
return ""+this.neighbours;

How does the recursion preorder traversal algorithm go back to parent?

public static void preorder(Node root) {
if(root == null) return;
root.printValue();
preorder(root.getLeft());
preorder(root.getRight());
}
I have tried to go through this function numerous times but i still can't figure out how after traversing through all the left children the algorithm goes back to the nearest ancestor(parent). Could someone explain this to me.
There's an implicit return at the end of your void method:
public static void preorder(Node root) {
if(root == null) return;
root.printValue();
preorder(root.getLeft());
preorder(root.getRight());
return;
}
You always return to the method that called you. So, if the parent's method call makes another call for the child, then when the child's method call returns, it returns to the parent's. Right?
Hope that makes sense. Like Kon said, you should just run through the algorithm on paper. Or, better yet, if you know how to use a debugger, you can just step through your code in the debugger and see how it works.
When traversal reaches leaf node, it's left and right children will be NULL. And preorder(root.getLeft()) will pass NULL and will return. Same way the right will also return NULL. Then the stack pops and goes back to parent.
I think it would be best if you can dry-run this using a stack then you'll be able to better understand it.

Categories

Resources