Intersecting 2 binary trees - throws Stack Overflow error [duplicate] - java

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Intersection of 2 binary trees throws Stack Overflow error
Java Binary Search Trees
I need to return a new OrderedSet that contains the overlapping elements of the two binary trees. I think it is the private OrderedSet that is throwing the error, at least that is what eclipse is telling me.
private OrderedSet<E> resultIntersect = new OrderedSet<E>();
public OrderedSet<E> intersection(OrderedSet<E> other) {
OrderedSet<E> result = new OrderedSet<E>();
result = resultIntersect;
return result;
}
private void intersection(OrderedSet<E> other, TreeNode t) {
if (other.contains(t.data)) {
resultIntersect.insert(t.data);
}
if(t.left != null)
intersection(other, t.left);
if(t.right != null)
intersection(other, t.right);
}
**EDIT
I can't seem to get it to return correctly. How can I get the private method to return the result correctly?
public OrderedSet<E> intersection(OrderedSet<E> other) {
OrderedSet<E> result = new OrderedSet<E>();
result = intersection(other, root, result);
return result;
}
private OrderedSet<E> intersection(OrderedSet<E> other, TreeNode t, OrderedSet<E> result) {
if (other.contains(t.data)) {
result.insert(t.data);
}
if (t.left != null && t.right != null)
return intersection(other, t.left, result) + intersection(other, t.right, result);
if (t.left != null)
intersection(other, t.left, result);
if (t.right != null)
return intersection(other, t.right, result);
else
return result;
}

I answered in your other question, but for completeness, here it is again.
Although you don't mention it, and your posted code did not include it, I'm guessing OrderedSet<E> resultIntersection is a field in OrderedSet<E>. In which case when you create a new instance of OrderedSet<E> it creates another instance of an OrderedSet<E> to assign to resultIntersection. That then has it's own resultIntersection that needs an instance of OrderedSet<E> creating, and so on...
The fix would be to remove resultIntersection and find some other way of implementing intersection. It's generally bad practice to have methods passing data around by manipulating shared state when it's not necessary, as it makes the logic more difficult to follow and can lead to multi-threading issues.

Related

(Java) Alphabetical binary tree class doesn't work as expected

I'm required to develop my own binary search tree class for an assignment. It's supposed to sort a data class (User) which contains two strings, a username and a password, and then later be allowed to search it against a text file by the username variable. I tested the files for the assignment using a TreeSet successfully and found that the search will return about 300 positive matches if the whole search file is used. I thought based on my prior work that my binary tree class would work, but no matter what it never finds any matches via its search function, even though I know that's incorrect.
Some info on the code that isn't shown here-
The node (called BinTree) contains a single user class as well as a left and right node, as is normal.
getUserName is a simple accessor which directly retrieves userName from the node's underlying User.
the grow method is initially called on a manually created root node (b = new BinTree([first data from the file reader is entered here])
Since this is a program handling usernames, case sensitivity is important.
Based on the way the driver class is written, the search method does need to only return true if it finds a User with the correct userName or false otherwise.
Here's the (new) grow() method;
public BinTree grow(BinTree root, BinTree newNode)
{
if (root == null)
return newNode;
if (newNode.getUserName().compareTo(root.getUserName()) > 0)
{
if (root.right == null)
root.right = newNode;
else
root.right.grow(root.right, newNode);
}
else
{
if (root.left == null)
root.left = newNode;
else
root.left.grow(root.left, newNode);
}
return root;
}
And here's the (new) search() method;
public boolean search(BinTree r, String s) //search for a username, case sensitive
{
if (r.getUserName().compareTo(s) == 0)
return true;
else if (r.left != null && r.getUserName().compareTo(s) < 0)
return r.left.search(r.left, s);
else if (r.right != null)
return r.right.search(r.right, s);
return false;
}
Like I said, I had done a simpler binary tree before (that only held single int values, not custom data classes) and I've tried various ways of writing these two methods, but I feel like there's one crucial piece of the puzzle I'm just not getting. Thanks in advance for any help.
UPDATE: Thanks to #Diasiare and #c0der for pointing out the obvious issue that my code had typos regarding returns and left/right. I took that and changed the above code to reflect it. Upon running, the program still didn't seem to be working. I then wrote this show() method to print all the usernames stored in the Users of the tree.
public void show(BinTree r)
{
if (r != null)
{
show(r.left);
System.out.println(r.getUserName());
show(r.right);
}
} // show
When I called it after updating and compiling everything else, it did in fact show that the list was populated with usernames in alphabetical order. Here's a small snippet of the output (there's a lot of lines)
ted#wisler.com
teddy#borglum.com
teddy#winkey.com
-->teodoro#harkin.com<--
teodoro#stranford.com
teodoro#talaska.com
teodoro#willette.com
tera#norise.com
tera#strevels.com
terence#lathrum.com
terence#morocco.com
terence#neidig.com
terence#rabago.com
teresa#chauvin.com
teresa#dockus.com
The one singled out with arrows is one I manually searched through the search .txt file for and found. All told, with this new information I've determined that A) grow() is working correctly, as it populates the tree from the file in alphabetical order, and B) search() should be returning at least one match. Therefore, I'm working under the assumption that the problem lies in search() still.
UPDATE2: Here's the context in which I'm calling search() for those interested.
try
{
BufferedReader fromPasswords = new BufferedReader(new FileReader("passwordInput.txt"));
while ((line = fromPasswords.readLine()) != null)
{
System.out.println(line);
if(b.search(b, line))
{
matches++;
}
}
fromPasswords.close();
}
catch (Exception e)
{
System.out.println("Error while searching tree: " + e);
System.exit(0);
}
Your main problem is that the search function doesn't return the result of the recursive calls, it should read something like the following:
public boolean search(BinTree r, String s) //search for a username, case sensitive
{
if (r.getUserName().compareTo(s) == 0)
return true;
else if (r.left != null && r.getUserName().compareTo(s) < 0)
return r.left.search(r.left, s);
else if (r.right != null)
return r.right.search(r.right, s);
return false;
}
I would also like to note that this:
if (root == null)
root = newNode;
Doesn't make a lot of sense. If root is null you end up inserting newNode as a child of itself. Additionally your method has no way to comunicate that the tree was empty. I would recommend that you throw an NullPointerException, or make the method return the new tree. This will help if in the future you want it to be a balanced tree, in which case the root node might change. In that case the solution would look like this:
public BinTree grow(BinTree root, BinTree newNode)
{
if (root == null)
return newNode;
if (newNode.getUserName().compareTo(root.getUserName()) > 0)
{
if (root.right == null)
root.right = newNode;
else
root.right.grow(root.right, newNode);
}
else
{
if (root.left == null)
root.left = newNode;
else
root.left.grow(root.left, newNode);
}
return root;
}
Additionally as c0der notes the line root.left.grow(root.right, newNode); should probably be root.left.grow(root.left, newNode);

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

Cleaner way to check if null [duplicate]

This question already has answers here:
How to get the first non-null value in Java?
(13 answers)
Closed 6 years ago.
I have a class that holds a couple of byte arrays, some could be empty some not. And I want to return the first non null array (if there is one) or null if there isn't. But the code just seems so redundant and ugly.
public byte[] getFirstPhoto() {
if (photo1 != null) {
return photo1;
}
if (photo2 != null) {
return photo2;
}
if (photo3 != null) {
return photo3;
}
if(videoThumbnail != null){
return videoThumbnail;
}
return null;
}
Is there anyway to clean that up, or not really?
Yes. Write a method something like this:
public byte[] firstNonNull(byte[]... arrays) {
for (byte[] array : arrays) {
if (array != null)
return array;
}
return null;
}
Then call this function passing in your four arrays in the proper order.

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.

Eclipse marks lines as dead code

I have this function with some dead code, marked by Eclipse.
I have two lines that check a & b. Lines that check b are marked as null.
public int[] runThis(List<Integer> buildIds, List<Integer> scenarios, boolean oflag) {
int rating[] = new int[scenarios.size()];
if(buildIds == null) {
System.out.println("ERROR - Building ID list is null!");
return null;
}
if(scenarios == null) {
System.out.println("ERROR - Scenario list is null!"); //dead
return null; //dead
}
return rating;
}
Why does Ellipse make the two lines as dead? Any help? Thanks very much for your time.
Because you've already called scenarios.size() in your array constructor. This guarantees scenarios isn't null or it will have thrown an exception by that point.

Categories

Resources