Notes for July 29, 2005

Faulty insert

    public void insert(Object next) {
        insert(next, root);
    }

    private void insert(Object next, SearchTreeNode node) {
        if (node == null) {
	    node = new SearchTreeNode(next);
	} else if (node.data.compareTo(next) < 0) {
	    insert(next, node.right);
	} else { // compareTo >= 0
	    insert(next, node.left);
	}
    }
Why doesn't this work? Whenever you call a method in Java, it copies the arguments into the parameter variables. Recall that all nonprimitive variables are addresses. They are not the objects themselves. So when you call a method, it copies the addresses into another variable, in this case next and node.

Suppose root is null (i.e., the tree is empty). The private method will be called with next and null (since root is null). In the private method, the first if clause will be satisfied, and the local variable node will have the address of a new SearchTreeNode. However, the root variable of the object will not see the change, because after the private method returns, node disappears. To fix this, we have the private method return that address, and then throughout the code, we have lines of the form:

variable = change(variable);
For the most part, when inserting or deleting something, most of these return values will be untouched. Most of the time, it'll be setting the variable to what it already was. The only time it makes a difference is when a change is occuring. It might seem pointless to have a lot of code equivalent to:
variable = variable;
but this is necessary for the change to occur where it matters.

removeLeaves

The following method removes the leaves from a binary tree.
    public void removeLeaves() {
        root = removeLeaves(root);
    }

    private TreeNode removeLeaves(TreeNode node) {
        if (node != null) {
	    if (node.left == null && node.right == null) {
	        node = null;
	    } else {
	        node.left = removeLeaves(node.left);
	        node.right = removeLeaves(node.right);
	    }
	}

	return node;
    }

delete

The delete method is described in detail in the reading. The code below is analogous to the code in the reading except in more familiar terms.
    public void delete(Object toDelete) {
        root = delete(root, toDelete);
    }

    private SearchTreeNode delete(SearchTreeNode node, Object toDelete) {
        if (node == null) {
	    return null;
	}

	if (node.data.compareTo(toDelete) == 0) {
	    if (node.left == null && node.right == null) {
	        return null;
	    } else if (node.left == null) { // only has right child
	        return node.right;
	    } else if (node.right == null) { // only has left child
	        return node.left;
	    } else { // has both children
	        SearchTreeNode tmp = smallestNode(node.right);
		node.data = tmp.data;
		node.right = delete(node.right, tmp.data);
	    }
	} else if (node.data.compareTo(toDelete) < 0) {
	    node.right = delete(node.right, toDelete);
	} else {
	    node.left = delete(node.left, toDelete);
	}

	return node;
    }

smallestNode

    // pre: node != null
    private SearchTreeNode smallestNode(SearchTreeNode node) {
        if (node.left == null) {
	    return node;
        } else { // node.left != null
	    return smallestNode(node.left);
	}
    }