I'd like to implement and test a tree search function. I'm using the DefaultTreeModel of javax.swing. I operato on my own defined Employee objects as the tree data. I tried to get the following to work:
private DefaultMutableTreeNode search(int id, DefaultMutableTreeNode node){
if(node != null){
Employee emp = (Employee) node.getUserObject();
if(emp.getId() == id){
return node;
} else {
DefaultMutableTreeNode foundNode = search(id, node.getPreviousSibling());
if(foundNode == null) {
foundNode = search(id, node.getNextSibling());
}
return foundNode;
}
} else {
return null;
}
}
but it can only find the element which is among one parent's siblings. And like to find it in entire tree, from root to leaves. How do I do it?
You could navigate to the root of the tree first in a different method, then call your current recursive method.
private DefaultMutableTreeNode search(int id, DefaultMutableTreeNode node){
if(node == null){
return null;
}
node = node.getRoot(); //Turns out that this class has a getRoot() method.
return searchInternal(id,node); //Then does a depth-first search from the root node.
}
private DefaultMutableTreeNode searchInternal(int id, DefaultMutableTreeNode node){
if(node == null){ //I also switched this around, for good practice.
return null;
}
Employee emp = (Employee) node.getUserObject();
if(emp.getId() == id){ //Found it!
return node;
}
DefaultMutableTreeNode foundNode = searchInternal(id, node.getPreviousSibling());
if(foundNode == null) {
foundNode = search(id, node.getNextSibling());
}
return foundNode;
}
In general, recursive methods tend to run a bit slower in Java, and they are prone to stack overflowing. It's usually better to use a custom stack for depth-first search, and this doesn't require you to make a different method. Recursive methods might make the code more readable in some cases.
What also works, in this case:
private DefaultMutableTreeNode search(int id, DefaultMutableTreeNode node){
if(node == null){
return null;
}
Enumeration enumeration = node.getRoot().depthFirstEnumeration(); //Enumerate over all nodes in the tree.
while(enumeration.hasMoreElements()){
DefaultMutableTreeNode next = enumeration.next();
if(((Employee)next.getUserObject()).getId() == id){ //Found it!
return next;
}
}
return null;
}
Turns out that depth-first searching is already implemented by Swing. This is a post-order enumeration though. See the javadoc, there are also methods for the other types of traversals.
Related
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;
}
I have a tree class that looks like:
Class Tree {
Node root;
Node curNode;
public List<String> find(String value) {
if (curNode == null) curNode = root;
for (Node child : curNode.children) {
if (found == false) {
if (child.data.equals(value)) {
// if it finds it return the path to this node.
}
curNode = child;
findDFS(value);
}
}
}
class Node {
List<Node> children;
String data;
}
Where the tree root contains pointers to children nodes which point to other children etc etc. What I'm having problems with is once it finds the node, I need to return the the path to that node.
passing a list tracking the path, once find the node, exit the recursion and fill the path one by one.
Boolean Search(Node node, String value, List<Node> track)
{
if (node == null) return false;
if (node.data.equals(value))
{
track.add(node);
return true;
}
for(Node child : node.children)
{
if (Search(child, value, track)
{
track.add(0, node);
return true;
}
}
return false;
}
If the nodes only point to their children, you'll need to keep track of each path on the way down. As mentioned in comment, you can do this with your own stack or with recursion. For example, you could always return find() call on the children of each node.
If the nodes point both ways, you can easily re-trace the path up once you find the correct node.
The following code traces the path, adding nodes to the list and removing them if they are not in the path
boolean getPath(Node root,String targetValue,List<Integer> path)
{
// base case root is null so path not available
if(root==null)
return false;
//add the data to the path
path.add(root.getData());
//if the root has data return true,path already saved
if(root.getData().equals(targetValue))
return true;
//find the value in all the children
for(Node child: children){
if(getPath(child,targetValue,path))
return true;
}
//if this node does not exist in path remove it
path.remove(path.size()-1);
return false;
}
I understand the algorithms but I am not sure how to put it into actual codes. Please help! And also please explain in details. I really want to understand this besides just copying down the answer. ;)
Here are my codes:
public boolean getLeftChild(){
Node insertNode = root;
while(insertNode!=null){
insertNode = insertNode.left;
}
return true;
}
public Boolean removeMin(){
Node insertNode = root;
Node parentNode =root;
if (insertNode.left ==null){
insertNode.right = parentNode;
insertNode = null;
}else if (getLeftChild() ==true && insertNode.right != null){
insertNode.left = null;
}else{
parentNode.left = insertNode.right;
}
return true;
}
First things first: For trees I highly recommend recursion.
Just one example:
getSmallestNode(Node node){
if(node.left != null){
return getSmallestNode(node.left)
}
return node;
}
For the deletion, there can be two cases if you want do delete the smallest (and therefore the "most left leaf" child) of a binary tree.
Case 1: The leaf has no child nodes, in that case just set the according entry in the parent to null (mostLeftChild.getParent().left = null)
Case 2: The leaf has a right child node (there can't be a left child node because that means there would be a smaller node and your currently selected node isn't the smallest) in that case you replace the current left node with the smallest node of the right subtree mostLeftChild.getParent().left = getSmallestFromSubtree(mostLeftChild.right)
So now to make that into code, it could look something like this (No guarantee that it really works)
public Node deleteSmallest(Node node){
// haven't reached leaf yet
if(node.left != null{
return deleteSmallest(node.left)
}
// case 1, no child nodes
if(node.right == null){
node.getParent().left = null;
} else { // case 2, right child node
node.getParent().left = deleteSmallest(node.right)
}
return node;
}
And you would call it with deleteSmallest(root)
I saw a lot of examples about Trees and how to recursively search them, but not like my case. So I decide to ask.
How can I find a path from any leaf to the root?
My problem is that I have a lot of child nodes per parent. Here is an example of my code:
private LinkedList<TreeNode> findPath(LinkedList<TreeNode> path, TreeNode root, TreeNode leaf){
if(root == null || root.name==null) return null;
path.add(root);
if(root.name.equals(leaf.name))
return path;
//Check if the leaf that we are looking for is one of the root children
if(root.children==null) return null;
for(TreeNode children : root.children){
if(children.name.equals(leaf.name)){
path.add(children);
return path;
}
}
//Search in all the childrens of the root recursively
for(TreeNode children : root.children){
LinkedList<TreeNode> result = findPath(path, children, leaf);
if(result != null)
return result;
}
//The leaf is not found.
return null;
}
And the problem is that every time when I check a child, if I don't find my leaf there I take back but I have add the child node in the path and my path becomes very big.
This implementation assumes that every tree node 'knows' its parent:
private List<TreeNode> findPath(TreeNode root, TreeNode leaf) {
List<TreeNode> path = new ArrayList<>();
TreeNode node = leaf;
do {
path.add(node);
node = node.getParent();
} while (node != root);
return path;
}
Of course you should add validity check for root and leaf and think of the possibility of an infinite loop if a node is (directly or indirectly) its own parent.
If your tree nodes only contain their children, but a child node does not 'know' its parent (which you probably should change if you own the code of the tree nodes), its getting more complex, as the tree must be searched recursively:
public static List<TreeNode> findPath(TreeNode root, TreeNode leaf) {
LinkedList<TreeNode> path = new LinkedList<>();
findPathHelper(root, leaf, path);
return path;
}
private static boolean findPathHelper(TreeNode root, TreeNode leaf, List<TreeNode> path) {
if (root == leaf) {
path.add(root);
return true;
}
for (TreeNode treeNode : root.children) {
if (findPathHelper(treeNode, leaf, path)) {
path.add(root);
return true;
}
}
return false;
}
I would like using my own Node class to implement tree structure in Java. But I'm confused how to do a deep copy to copy a tree.
My Node class would be like this:
public class Node{
private String value;
private Node leftChild;
private Node rightChild;
....
I'm new to recursion, so is there any code I can study? Thank you!
try
class Node {
private String value;
private Node left;
private Node right;
public Node(String value, Node left, Node right) {
this.value = value;
...
}
Node copy() {
Node left = null;
Node right = null;
if (this.left != null) {
left = this.left.copy();
}
if (this.right != null) {
right = this.right.copy();
}
return new Node(value, left, right);
}
}
Doing it recursively using pre-order traversal.
public static Node CopyTheTree(Node root)
{
if (root == null)
{
return null;
}
Node newNode = new Node(null, null, root.Value);
newNode.Left= CopyTheTree(root.Left);
newNode.Right= CopyTheTree(root.Right);
return newNode;
}
You can use something like this. It will go though the old tree depth first wise and create a copy of it.
private Tree getCopyOfTree(oldTree) {
Tree newTree = new Tree();
newTree.setRootNode(new Node());
copy(oldTree.getRootNode(), newTree.getRootNode())
return newTree;
}
private void copy(Node oldNode, Node newNode) {
if (oldNode.getLeftChild != null) {
newNode.setLeftChild(new Node(oldNode.getLeftChild()));
copy(oldNode.getLeftChild, newNode.getLeftChild());
}
if (oldNode.getRightChild != null) {
newNode.setRightChild(new Node(oldNode.getRightChild()));
copy(oldNode.getRightChild, newNode.getRightChild());
}
}
I like Evgeniy Dorofeev's answer above, but sometimes you might not be able to add a method to the type Node as you might not own it. In that case(this is in c#):
public static TreeNode CopyTree(TreeNode originalTreeNode)
{
if (originalTreeNode == null)
{
return null;
}
// copy current node's data
var copiedNode = new TreeNode(originalTreeNode.Data);
// copy current node's children
foreach (var childNode in originalTreeNode.Children)
{
copiedNode.Children.Add(CopyTree(childNode));
}
return copiedNode;
}
Not sure but try something with post order traversal of your tree and creating a new node for each node you traverse. You might require stack for storing the nodes you created to make left and right child links.
public static TreeNode copy( TreeNode source )
{
if( source == null )
return null;
else
return new TreeNode( source.getInfo( ), copy( source.getLeft( ) ), copy( source.getRight( ) ) );
}
/Sure. Sorry for the delay. Anyway... any recursive method has a base case, and one or more recursive cases. In this instance, the first line is obvious... if the argument to the parameter 'source' is null (as it eventually evaluates to in order to end the method's operation), it will return null; if not, the recursive case is initiated. In this case, you're returning the entire copied tree once the recursion is complete.
The 'new' operator is used, indicating the instantiation of a TreeNode with each visit to the various nodes of the tree during the traversal, occurring through recursive calls to 'copy', whose arguments become references to the left and right TreeNodes (if there are any). Once source becomes null in each argument, the base case is initiated, releasing the recursion stack back to the original call to 'copy', which is a copy of the root of the original tree./
Node copy(Node node)
{
if(node==null) return node;
Node node1 =new Node(node.data);
node1.left=copy(node.left);
node1.right=copy(node.right);
return node1;
}