1

I am wondering how I can switch my remove method from being recursive to being iterative. My recursive method is working perfectly fine, but all my attempts at making it iterative are not. Where am I going wrong and how can I fix it?

So here's my recursive method:

public boolean remove(E someElement) {
    return remove(root, someElement);
}
private boolean remove(Node<E> node, E dataItem) {
    if (node == null) {
        return false;
    }
    int val = dataItem.compareTo(node.data);
    if (val < 0)
        return remove(node.left, dataItem);
    else if (val > 0)
        return remove(node.right, dataItem);
    else
        return false;
}
Shankha057
  • 1,296
  • 1
  • 20
  • 38
A aqua
  • 15
  • 1
  • 7
  • 2
    The nature of a BST is such that you don't really operate on it iteratively. It would kind of defeat the point of using a BST, the point of which is that it can do operations that are faster and easier than using iteration. What were you planning on iterating over? Also you might realize this already but your algorithm doesn't actually delete nodes from the tree, i.e. there's no case for when the algorithm actually finds the node in question. See here for a description of the remove (or "delete") operation on a BST: https://www.geeksforgeeks.org/binary-search-tree-set-2-delete/ –  Nov 14 '19 at 18:27
  • Note that your method is not doing anything – Maurice Perry Nov 15 '19 at 06:37
  • You can't do that: if you remove a node, you may need to change the `root`, but you're passing it by value. It's much easier to do this kind of things in C/C++ because of the possibility to get a pointer to a variable. – Maurice Perry Nov 15 '19 at 06:58

3 Answers3

0

I'll give you the algorithm, you can try to code it yourself.
You can use a Stack to iterate through the tree.
So here's how you iterate:

  1. push the tree to stack
  2. loop until the stack isn't empty
  3. pop a node
  4. Null check. If null then continue.
  5. push the left and the right sub-tree onto the Stack

Now in the midst of the iteration, you simply need to check if the popped node is the one you are looking for.
Yes? Check if it has children or not.

  1. Has children? Implement the children snatching logic as usual for recursive deletion
  2. Doesn't have children (a.k.a. leaf node)? Simply assign it to null
    Break

No? Continue iterating

Although I feel that Trees are by nature recursive and using recursion is simply a better choice in terms of boosting conceptual understanding of the general working principal of this data structure.

Shankha057
  • 1,296
  • 1
  • 20
  • 38
0

As noted in comments, remove as it is now does nothing, and can be safely replaced with return false;.

Assuming that in the else case you want to do something sensible, as in

private boolean remove(Node<E> node, E dataItem) {
    if (node == null) {
        return false;
    }
    int val = dataItem.compareTo(node.data);
    if (val < 0)
        return remove(node.left, dataItem);
    else if (val > 0)
        return remove(node.right, dataItem);
    else
        return do_something(node);
}

the standard strategy is to transform it into a tail recursion. Consolidate the multiple recursive calls into a single one, and make it a last statement in the function:

private boolean remove(Node<E> node, E dataItem) {
    if (node == null) {
        return false;
    }
    int val = dataItem.compareTo(node.data);
    if (val == 0) {
        return do_something(node);
    }

    if (val < 0)
        node = node.left;
    else
        node = node.right;

    return remove(node);
}

So far, just a rewrite to achieve a tail recursive form.

Now, any tail recursive function

foo(args) {
    if (interesting_condition(args)) {
        return do_something_important(args);
    }

    args = recompute_arguments(args);

    return foo(args);
}

could be mechanically transformed into iterative:

foo(args) {
    while (!interesting_condition(args)) {
        args = recompute_arguments(args);
    }
    return do_something_important(args);
}

I hope I answered your question.

user58697
  • 7,808
  • 1
  • 14
  • 28
0

BST manipulation is much easier to do iteratively in C/C++ than in Java because of the possibility to get a pointer to a variable.

In Java, you need to treat differently the case where the element is found at the root; in all other cases the node you're considering is either at the left or at the right of it's parent; so you can replace C's pointer (or reference) to pointers with the parent node and a boolean indicating at which side of the parent the current node is:

public boolean remove(E someElement) {
    if (root == null) {
        return false;
    }
    int val = someElement.compareTo(root.data);
    if (val < 0) {
        return remove(root, false, someElement);
    } else if (val > 0) {
        return remove(root, true, someElement);
    } else {
        root = removeNode(root);
        return true;
    }
}

private boolean remove(Node<E> parent, boolean right, E dataItem) {
    Node<E> node = right ? parent.right : parent.left;
    if (node == null) {
        return false;
    }
    int val = dataItem.compareTo(node.data);
    if (val < 0) {
        return remove(node, false, dataItem);
    } else if (val > 0) {
        return remove(node, true, dataItem);
    } else {
        node = removeNode(node);
        if (right) {
            parent.right = node;
        } else {
            parent.left = node;
        }
        return true;
    }
}

I have omitted method removeNode for the time being, right now, we can make the second method iterative:

private boolean remove(Node<E> parent, boolean right, E dataItem) {
    while (true) {
        Node<E> node = right ? parent.right : parent.left;
        if (node == null) {
            return false;
        }
        int val = dataItem.compareTo(node.data);
        if (val < 0) {
            right = false;
        } else if (val > 0) {
            right = true;
        } else {
            node = removeNode(node);
            if (right) {
                parent.right = node;
            } else {
                parent.left = node;
            }
            return true;
        }
        parent = node;
    }
}

Now the method removeNode must remove the top node and return the new top node after removal. If either left or right is null, it can just return the other node, otherwise, we must find a node to replace the topnode, and it can be either the rightmost node of the left subtree, or the leftmode node of the right subtree.

private Node<E> removeNode(Node<E> parent) {
    if (parent.left == null) {
        return parent.right;
    } else if (parent.right == null) {
        return parent.left;
    }
    boolean right = random.nextBoolean();
    Node<E> node = right ? parent.right : parent.left;
    Node<E> last = removeLast(node, !right);
    if (last == null) {
        if (right) {
            node.left = parent.left;
        } else {
            node.right = parent.right;
        }
        return node;
    } else {
        last.left = parent.left;
        last.right = parent.right;
        return last;
    }
}

private Node<E> removeLast(Node<E> parent, boolean right) {
    Node<E> node = right ? parent.right : parent.left;
    if (node == null) {
        return null;
    }
    while (true) {
        Node<E> next = right ? node.right : node.left;
        if (next == null) {
            break;
        }
        parent = node;
        node = next;
    }
    if (right) {
        parent.right = node.left;
        node.left = null;
    } else {
        parent.left = node.right;
        node.right = null;
    }
    return node;
}
Maurice Perry
  • 9,261
  • 2
  • 12
  • 24