二叉树节点删除Java的问题

时间:2015-11-28 19:39:26

标签: java data-structures binary-search-tree

我想尝试将自己的BST作为练习来实现,但我仍然坚持删除节点。我无法弄清楚为什么它在某些情况下不起作用。我从一本书中获取算法,但是当我用随机元素测试它时,有些情况下它不会删除元素,甚至会弄乱它们的顺序,因此它们不再被排序。我做错了什么以及什么是更好的方法来实现这个目标?

注意:方法中的所有println()语句仅用于调试目的

class TreeNode<T extends Comparable<T>> {
    T data;
    TreeNode<T> left;
    TreeNode<T> right;
    TreeNode<T> parent;

    TreeNode(T data) {
        this.data = data;
    }

    boolean hasBothChildren() {
        return hasLeftChild() && hasRightChild();
    }

    boolean hasChildren() {
        return hasLeftChild() || hasRightChild();
    }

    boolean hasLeftChild() {
        return left != null;
    }

    boolean hasRightChild() {
        return right != null;
    }

    boolean isLeftChild() {
        return this.parent.left == this;
    }

    boolean isRightChild() {
        return this.parent.right == this;
    }
}

public class BinaryTreeSet<T extends Comparable<T>> {
    private TreeNode<T> root;

    private void makeRoot(T element) {
        TreeNode<T> node = new TreeNode<T>(element);
        root = node;
    }

    private TreeNode<T> find(T element) {
        TreeNode<T> marker = root;
        TreeNode<T> found = null;

        while (found == null && marker != null) {
            int comparator = (marker.data.compareTo(element));

            if (comparator > 0)
                marker = marker.left;
            else if (comparator < 0)
                marker = marker.right;
            else
                found = marker;
        }

        return found;
    }

    private TreeNode<T> max(TreeNode<T> root) {
        TreeNode<T> currentMax = root;

        while (currentMax.hasRightChild()) {
            currentMax = currentMax.right;
        }

        return currentMax;
    }

    // returns the inorder predecessor of node
    private TreeNode<T> predecessor(TreeNode<T> node) {
        return max(node.left);
    }

    // removes a given node with 0 or 1 children
    private void removeNode(TreeNode<T> node) {
        if (!node.hasChildren()) {
            System.out.println("node with no children");
            if (node.isLeftChild())
                node.parent.left = null;
            else
                node.parent.right = null;
        }
        else {
            System.out.println("node with 1 child");
            if (node.isRightChild()) {
                if (node.hasLeftChild())
                    node.parent.right = node.left;
                else if (node.hasRightChild())
                    node.parent.right = node.right;
            }
            else if (node.isLeftChild()) {
                if (node.hasLeftChild())
                    node.parent.left = node.left;
                else if (node.hasRightChild())
                    node.parent.left = node.right;
            }
        }

        node = null;
    }

    public BinaryTreeSet() {
        root = null;
    }

    public void addElement(T element) {
        if (root == null)
            makeRoot(element);
        else {
            TreeNode<T> marker = root;
            TreeNode<T> node = new TreeNode<T>(element);
            boolean done = false;

            while(!done) {
                int comparator = marker.data.compareTo(element);
                if (comparator > 0) {
                    if (marker.hasLeftChild())
                        marker = marker.left;
                    else {
                        marker.left = node;
                        done = true;
                    }
                }
                else if (comparator < 0) {
                    if (marker.hasRightChild())
                        marker = marker.right;
                    else {
                        marker.right = node;
                        done = true;
                    }
                }
                else
                    return;

                node.parent = marker;
            }
        }
    }

    public boolean contains(T element) {
        boolean found = (find(element) == null)? false : true;
        return found;
    }

    public boolean removeElement(T element) {
        TreeNode<T> node = find(element);
        if (node == null)
            return false;

        // removal of a node with no children
        if (!node.hasChildren()) {
            if (node.isLeftChild()) {
                node.parent.left = null;
            }
            else if (node.isRightChild()) {
                node.parent.right = null;
            }
        }

        // removal of a node with both children
        else if (node.hasBothChildren()) {
            TreeNode<T> pred = predecessor(node);
            T temp = pred.data;
            pred.data = node.data;
            node.data = temp;
            removeNode(pred);
        }

        // removal of a node with only 1 child
        else {
            if (node.isRightChild()) {
                if (node.hasLeftChild())
                    node.parent.right = node.left;
                else if (node.hasRightChild())
                    node.parent.right = node.right;
            }
            else if (node.isLeftChild()) {
                if (node.hasLeftChild())
                    node.parent.left = node.left;
                else if (node.hasRightChild())
                    node.parent.left = node.right;
            }

        }

        node = null;
        System.out.println("item removed: " + !contains(element));
        return true;
    }
}

2 个答案:

答案 0 :(得分:1)

发现导致我头痛的错误。问题是在我删除0或1个子节点的情况下。我没有更新他们的父节点,所以搞砸了代码。所以,而不是,例如

if (node.hasLeftChild())
    node.parent.right = node.left;

我应该写

if (node.hasLeftChild()) {
    node.parent.left = node.left;
    node.left.parent = node.parent;
}

在我处理单个孩子的所有情况下。另外,当root是removeElement()函数的目标时,我忘了更新根。 但是,就目前而言,我觉得我在代码中有很多重复。我还没有想出更优雅的解决方案。

编辑:还有其他小错误,主要在isRightChild()isLeftChild()函数中,如果相关节点没有父级,则会生成NullPointerException

答案 1 :(得分:1)

请将以下方法添加到BinaryTreeSet类中并调用它, 这将显示带有左/右前缀的当前元素列表。

 boolean rootOncePrint = true;
    public void printAllTree(TreeNode<T> startNode){
        if(startNode == null) return;
        //System.out.println(startNode.data);
        if(rootOncePrint){
            System.out.println("Root : " + startNode.data);
            rootOncePrint = false;
        }

        if(startNode.hasChildren()){
            if(startNode.hasLeftChild()){
                printAllTree(startNode.left);
            } 
            if(startNode.hasRightChild()){
                printAllTree(startNode.right);
            }

        }
        if(startNode != root){
            T parentValue = startNode.parent.data;
            T dataValue = startNode.data; 
            System.out.println(startNode.data + ((parentValue.compareTo(dataValue) > 0)?"L":"R"));
        }

    }
  

添加此代码后,尝试在BinaryTreeSet中添加/删除元素   你会知道发生了什么。