Recursive search through tree without passing object - java

I'm trying to search for a node in a non-binary tree without actually passing a node to the search method.
Each node has a name variable. The findChild() method takes a name, and searches through the tree it was called on to find the node with that name.
To do the recursive search, I call findChild() on the child node rather than passing the child node to the findChild() method. Print statements show me that the method gets down through the tree, but the result variable gets set to null as the stack is unwinded, so the method always returns null. I understand why it's doing this, but I don't understand how to unwind this type of recursion. Any help is appreciated!
My findChild() method:
public FileNode findChild(String name) {
FileNode result = null;
for (FileNode child : this.getChildren()) {
if (child.getName() == name) {
return child;
} else {
child.findChild(name);
}
}
return result;
}

Will the following small change help? Your else condition is never assigning a value.
public FileNode findChild(String name) {
FileNode result = null;
for (FileNode child : this.getChildren()) {
if (child.getName() == name) {
result = child;
break;
} else {
result = child.findChild(name);
if (result != null)
break;
}
}
return result;
}

You're throwing away the result of FileNode#findChild in the else block
Try this
if (child.getName().equals(name)) {
return child;
} else {
FileNode childResult = child.findChild(name);
if (childResult != null) {
return childResult;
}
}

Related

How can I iterate throughout on list of nested java objects?

I have an object class which is contains a list of itself... Something like this:
public class SearchItemType implements Serializable {
protected List<SearchItemType> childItem;
}
The childItem also can conatain list of child items. My question is, can I iterate over childItems in all levels?
Right now my code looks like this:
public SearchItemType getElementByOpenedRowID(SearchItemType gridResult, String selectedRowId, Boolean found) {
SearchItemType element = new SearchItemType();
if (gridResult.getId().equals(selectedRowId)) {
element = gridResult;
found = true;
}
for (SearchItemType child : gridResult.getChildItem()) {
if (child.getId().equals(selectedRowId)) {
element = child;
found = true;
break;
}
}
if (!found) {
for (SearchItemType child : gridResult.getChildItem()) {
element = getElementByOpenedRowID(child, selectedRowId, found);
checkChildID(child, selectedRowId);
if (element != null) break;
}
}
return element;
}
Many thanks.
There is one error: at the start of the method, you set SearchItemType element = new SearchItemType(); but then check for null when you recurse. element will never be null. You can fix this by setting it to null at the start, but I have some suggestions about your code:
Instead of assigning the found value to an element and setting a found flag, just return the object as soon as you find it. At the end of the method return null. This will be much clearer.
Iterating over the children and checking them will currently be executed even if the parent was the one that was searched for. In fact, you can remove this loop entirely, as it is handled by the recursive step below.
Why are you passing found as a parameter? If you pass it true, then there is no point in having it, so if you really need it, just instantiate it in the method.
Make sure to check that gridResult is not null. You could remedy this by making getElementByOpenedRowID a method on SearchItemType, meaning that gridResult does not need to be passed.
Applying these changes will result in:
public SearchItemType getElementByOpenedRowID(SearchItemType gridResult, String selectedRowId) {
// stop at null
if (gridResult == null) {
return null;
}
if (gridResult.getId().equals(selectedRowId)) {
return gridResult; // return once found
}
// check all of the children
for (SearchItemType child : gridResult.getChildItem()) {
// do the search again for every child
SearchItemType result = getElementByOpenedRowID(child, selectedRowId);
if (result != null) {
// return once found and sent it all the way to the top
return result;
}
}
return null;
}
You can do this with recursion:
public void iterate(SearchItemType type) {
// Do something with type
for (SearchItemType child in type.childItem) {
iterate(child);
}
}
Yes you can iterate on childItem object at any level as long as childItem is not null and object inside it has non-null values.
In Data structure implementation of LinkedList every node in the LinkedList has Data fields link to other nodes (In case of Java it's reference to other nodes).
It's also called as self referencing objects that means object pointing to object of similar type.
As long as you have non-null values in the list you can iterate at any level.
Data structures in Java are implemented in similar manner.
Have look at Node class in this code snippet:
Linked List implementation using self referencing pointers
You want to iterate through the children recursively as so:
public SearchItemType getElementByOpenedRowID(SearchItemType gridResult, String selectedRowId) {
SearchItemType element = null;
if (gridResult == null) return null;
else if (gridResult.getId().equals(selectedRowId)) return gridResult;
else {
for (SearchItemType child : gridResult.getChildItem()) {
element = getElementByOpenedRowID(child, selectedRowId);
if (element != null) break;
}
}
return element;
}

How can I return a node with a specific value in a BST?

I have to make a so called "Hit Balanced Tree". The difference is that as you can see, my node class has an instance variable called numberOfHits, which increments anytime you call contains method or findNode method. The point of this exercise is to have the nodes with highest hit count on the top, so the tree basically reconstructs itself (or rotates). Root has the highest hit count obviously.
I have a question regarding a method I have to make, that returns the node with highest hit count. I will later need it to make the tree rotate itself (I guess, at least that's the plan). Here is my node class. (All the getters of course)
public class HBTNode<T> {
private HBTNode<T> left;
private HBTNode<T> right;
private T element;
private int numberOfHits;
public HBTNode(T element){
this.left = null;
this.right = null;
this.element = element;
this.numberOfHits = 0;
}
What I have so far is this:
public int findMaxCount(HBTNode<T> node) {
int max = node.getNumberOfHits();
if (node.getLeft() != null) {
max = Math.max(max, findMaxCount(node.getLeft()));
}
if (node.getRight() != null) {
max = Math.max(max, findMaxCount(node.getRight()));
}
return max;
}
This works fine, except it returns an integer.I need to return the node itself. And since I have to do this recursively, I decided find the biggest hit count and then subsequently using this method in another method that returns a node, like this(it's probably really inefficient, so if you have tips on improvement, I am listening):
public int findMaxCount() {
return findMaxCount(root);
}
public HBTNode<T> findMaxCountNode(HBTNode<T> node) {
if (node.getNumberOfHits() == this.findMaxCount()) {
return node;
}
if (node.getLeft() != null ) {
return findMaxCountNode(node.getLeft());
}
if (node.getRight() != null) {
return findMaxCountNode(node.getRight());
}
return null;
}
I call the method like this:
public HBTNode<T> findMaxCountNode() {
return findMaxCountNode(root);
}
It returns null even though I think it should be fine, I am not that good at recursion so obviously I am missing something. I am open to any help, also new suggestions, if you have any about this exercise of mine. Thanks a lot.
Test code:
public static void main(String[] args) {
HBTree<Integer> tree = new HBTree<Integer>();
tree.add(50);
tree.add(25);
tree.add(74);
tree.add(19);
tree.add(8);
tree.add(6);
tree.add(57);
tree.add(108);
System.out.println(tree.contains(108)); //contains method increases the count by one
System.out.println(tree.contains(8));
System.out.println(tree.contains(8));
System.out.println(tree.contains(108));
System.out.println(tree.contains(8));
System.out.println(tree.contains(108));
System.out.println(tree.contains(108));
System.out.println(tree.contains(108));
System.out.println(tree.findMaxCountNode());
}
Current output: true
true
true
true
true
true
true
true
null
Expected output: true
true
true
true
true
true
true
true
Element: 108
Left child: 6 //this is just a toString, doesn't matter at this point
Right child: null
Number of hits: 5
Seems like your two functions should look like the following. What I'm assuming here is that these functions, which are defined inside the HBTNode class, are designed to find the highest hit-count node below itself:
public HBTNode<T> findMaxCountNode(HBTNode<T> node) {
return findMaxCountNode(node, node);
}
public HBTNode<T> findMaxCountNode(HBTNode<T> node, HBTNode<T> maxNode) {
HBTNode<T> currMax = (node.getNumberOfHits() > maxNode.getNumberOfHits()) ? node : maxNode;
if (node.getLeft() != null ) {
currMax = findMaxCountNode(node.getLeft(), currMax);
}
if (node.getRight() != null) {
currMax = findMaxCountNode(node.getRight(), currMax);
}
return currMax;
}
public int findMaxCount(HBTNode<T> node) {
HBTNode<T> maxNode = findMaxCountNode(node);
if (maxNode != NULL)
return maxNode.getNumberOfHits();
else
return -1;
}
Let me know if there are any issues, this is off the top of my head, but I thought it would be helpful to point out that the "integer" version of your method should just use the "Node finding" version of the method. The method you wrote to find the maximum value is quite similar to the one I wrote here to find the maximum node.

Java: What's incorrect with my general tree traversal implementation?

I've been tasked with finding and returning a particular node from a general tree given by the string targetName. Take a look at my implementation below:
public GeneralTreeNode findNode(String targetName) {
if (this.name.equals(targetName)) {
return this;
} else {
for (GeneralTreeNode child : this.children) {
return child.findNode(targetName);
}
}
// no node containing the string could be found
return null;
}
The only problem is that this too often seems to incorrectly return null when in fact a node does exist. It's as if the last line, return null, is too greedy.
Chucking a few breakpoints on this and watching it shows it only seems to go down to the lowest depth until a node has no children, in which case it simply returns null.
Can anyone offer suggestions on how to improve this?
Change your code to this:
public GeneralTreeNode findNode(String targetName) {
if (this.name.equals(targetName)) {
return this;
} else {
for (GeneralTreeNode child : this.children) {
GeneralTreeNode childResult = child.findNode(targetName);
if (childResult != null) {
return childResult; // only return if you really found something
}
}
}
// no node containing the string could be found
return null;
}
You only want to return the result from the child search if it really found something.
The readability of the code disappears if you implement in this way. Tree-traversal would be better implemented in an helper class and you pass the ROOT element with the target_name together.
If you return null in this way, it is something like node is null actually it is not. On the other hand, when you use "doer" or "processor" method/function, it can return true saying "I cannot find anything".
Still, your code seems ok. I just rewrite it.
static Node search(Node node, String nodeName) {
if (node.getName().equals(nodeName)) return node;
List<Node> children = node.getChildren();
foreach(Node child : children) {
Node resultChild = search(child, nodeName);
if (resutlChild != null) return resultChild;
}
return null;
}

How to search a tree with three children nodes?

My tree nodes have the 3 string fields and 3 node fields which are left, middle and right.
One of the problems is that the method can only take string as a parameter
This is what I have
public TreeNode findNode(String name) {
TreeNode pointer = this.getRoot();
if (pointer.getName().equals(name))
return pointer;
if (pointer.getLeft() != null)
pointer = pointer.getLeft();
findNode(name);
if (pointer.getMiddle() != null)
pointer = pointer.getMiddle();
findNode(name);
if (pointer.getRight() != null)
pointer = pointer.getRight();
findNode(name);
return null;
}
This causes a stack overflow error because I just keep setting the pointer to root. But I have to start somewhere and my only parameters for the method can be name. I can't seem to see how to do this.
You can use a list as a parameter stack.
public TreeNode findNode(String name) {
List<TreeNode> stack = new ArrayList<TreeNode>();
stack.add(this.getRoot());
while (!stack.isEmpty())
{
TreeNode node = stack.remove(0);
if (node.getName().equals(name))
return node;
if (pointer.getLeft() != null)
stack.add(node.getLeft());
if (node.getMiddle() != null)
stack.add(node.getMiddle());
if (node.getRight() != null)
stack.add(node.getRight());
}
return null;
}
You can remove from the end of the list instead of the front of the list if you want to search depth-first.
Im guessing you cannot change the signature of this function. Have a helper function that takes in two parameters, (Node and name) that you call with root and name.
In all three cases (left, middle, right), you are calling findNode(name) but not for those objects, instead it is for this. That's why you get stack overflow.
Use an auxiliary method that takes in a TreeNode parameter in addition to the string:
public TreeNode findNode(String name) {
return auxFindNode(this.getRoot(), name);
}
private TreeNode auxFindNode(TreeNode node, String name) {
//perform your recursive traversal here
}
Your code as it stands will never work because you keep setting pointer to the root of the tree at the beginning of the method. So all your recursive calls start with the root of the tree.
If you prefer not to use another method, you can traverse the tree iteratively by using stack:
public TreeNode findNode(String name) {
Stack<TreeNode> stack = new Stack<TreeNode>();
TreeNode foundNode = null;
while(!stack.empty() && foundNode == null) {
TreeNode node = stack.pop();
if(node.getName().equals(name)) {
foundNode = node;
} else {
if(node.getLeft() != null) {
stack.push(node.getLeft();
}
if(node.getMiddle() != null) {
stack.push(node.getMiddle());
}
if(node.getRight() != null) {
stack.push(node.getRight());
}
}
}
return foundNode;
}

How to "delete" a node from a binary search tree in Java?

I made a binary search tree in Java but I'm having troubles whit the deleting nodes part. I managed to erase the node when it has only 1 son, and I have the idea to make the deletion when it has 2 sons, anyways the method I'm using when it has no sons (when it's a leaf) is not working in Java. Normally in C++ I would assign the Node "null" but it doesn't work here.
if (numberOfSons(node) == 0) {
node= null;
return true;
}
That's the portion of the code that takes care of the nulling part. When I debug it, it is referencing the correct node and it's assigning it the null value, but when I return to the Frame where I'm calling the delete method for my tree the node is still there. What's the correct way to "null" an object in Java? I thought everything was a pointer in here and therefore this would work, but I think it doesn't.
When you're nulling something you just make the reference in the scope you're in null. It doesn't affect anything outside.
Let me explain by example. Say you have a method foo:
public void foo(Node node) {
node = null;
if(node == null) {
System.out.println("node is null");
} else {
System.out.println("node is not null");
}
}
Now you call it like this:
public void doSomething() {
Node node = new Node();
foo(node);
if(node == null) {
System.out.println("Original node is null");
} else {
System.out.println("Original node is not null");
}
}
In your console you'll get:
node is null
original node in not null
The reason is that it's not a pointer, it's a reference. When you're nulling a reference, you just say "make this reference synonym to null". It doesn't mean that the object is deleted, it may still exist in other places. There is no way to delete objects in java. All you can do is make sure no other object points to them, and the garbage collector will delete the objects (sometime).
Nothing remains but to reinsert either left or right subtree. For instance:
class BinaryTree<T extends Comparable<T>> {
class Node {
Node left;
Node right;
T value;
}
Node root;
void delete(T soughtValue) {
root = deleteRec(root, soughtValue);
}
Node deleteRec(Node node, T soughtValue) {
if (node == null) {
return null;
}
int comparison = soughtValue.compareTo(node.value);
if (comparison < 0) {
node.left = deleteRec(node.left, soughtValue);
} else if (comparison > 0) {
node.right = deleteRec(node.right, soughtValue);
} else {
if (node.left == null) {
return node.right;
} else if (node.right == null) {
return node.left;
} else {
// Two subtrees remain, do for instance:
// Return left, with its greatest element getting
// the right subtree.
Node leftsRightmost = node.left;
while (leftsRightmost.right != null) {
leftsRightmost = leftsRightmost.right;
}
leftsRightmost.right = node.right;
return node.left;
}
}
return node;
}
}
As Java does not have aliases parameters as in C++ Node*& - a kind of in-out parameter, I use the result of deleteRec here. In java any function argument that is an object variable will never change the variable with another object instance. That was one of the language design decisions like single inheritance.

Categories

Resources