为二叉搜索树实现splay()方法

时间:2015-10-02 23:40:47

标签: java loops rotation infinite

我正在尝试为二叉搜索树实现splay(Node x)方法。我正确地实现了leftRotation(Node x)和rightRotation(Node x)方法(至少,我认为它们是...),但是当我尝试在splay(Node x)方法中实现它们时,它会调用自己无限循环。现在,我知道它为什么这样做,但似乎无法弄清楚如何修复它。

这是leftRotation(Node x)方法:

public void leftRotation(Node<E> x) {
    if (x.getRightChild() == null) {
        return;
    }

    Node<E> y = x.getRightChild();
    x.setRightChild(y.getLeftChild());

    if (y.getLeftChild() != null) {
        y.getLeftChild().setParent(x);
    }

    y.setParent(x.getParent());

    if (x.getParent() == null) {
        root = y;
    } else {
        if (x == x.getParent().getLeftChild()) {
            x.getParent().setLeftChild(y);
        } else {
            x.getParent().setRightChild(y);
        }
    }

    y.setLeftChild(x);
    x.setParent(y);
}

这里是rightRotation(节点x)方法:

public void rightRotation(Node<E> x) {
    if (x.getLeftChild() == null) {
        return;
    }

    Node<E> y = x.getLeftChild();
    x.setRightChild(y.getRightChild());

    if (y.getRightChild() != null) {
        y.getRightChild().setParent(x);
    }

    y.setParent(x.getParent());

    if (x.getParent() == null) {
        root = y;
    } else {
        if (x == x.getParent().getRightChild()) {
            x.getParent().setRightChild(y);
        } else {
            x.getParent().setLeftChild(y);
        }
    }

    x.setRightChild(x);
    x.setParent(y);
}

这是splay(Node x)方法:

public void splay(Node<E> x) {
    while (x.getParent() != null) {
        if (x.isLeftChild && x.getParent().isLeftChild) {
            this.rightRotation(x.getParent());
            this.rightRotation(x);
        } else if (x.isRightChild && x.getParent().isRightChild) {
            this.leftRotation(x.getParent());
            this.leftRotation(x);
        } else if (x.isLeftChild && x.getParent().isRightChild) {
            this.rightRotation(x);
            this.leftRotation(x);
        } else if (x.isRightChild() && x.getParent().isLeftChild()) {
            this.leftRotation(x);
            this.rightRotation(x);
        } else if (x.isLeftChild && x.getParent() == root) {
            this.rightRotation(x);
        } else if (x.isRightChild && x.getParent() == root) {
            this.leftRotation(x);
        }
    }
}

关于如何修复无限循环的任何想法?它似乎与splay(Node x)方法中的while(x.getParent()!= null)语句不一致,但是当我使用调试器完成代​​码时,它的属性节点似乎在变化,所以我真的不知道它出了什么问题?

setLeftChild(Node leftChild)方法:

public void setLeftChild(Node<E> leftChild) {
        this.leftChild = leftChild;

        if (leftChild != null) {
            leftChild.setIsRightChild(true);
            leftChild.setParent(this);
        }
    }

1 个答案:

答案 0 :(得分:0)

除了我在代码中指出的所有错误/错误之外,这里是rightRotation中最大的错误/错误:

x.setRightChild(x);

这会在树中创建一个循环,因此会产生无限循环。你应该对你的方法进行单元测试。您的代码中的另一个主要错误是您的if - else if指令没有else,因此可能会出现迭代期间没有任何反应的情况......因此无限循环。这不是这里的情况,因为你考虑了所有的情况(实际上,你考虑的更多,最后两个将永远不会被执行,因为前四个案例涵盖了所有可能的情况)但作为一般评论,这是以这种方式编码非常危险。

以下是我自己实现所有这些方法的代码,我认为它更清晰:

public class BinaryTree<T extends Comparable<T>> {
    private Node<T> root;

    public void rebalance(Node<T> node) {
        while (!node.isRoot()) rotation(node.getParent(), node.getChildKind().opposite());
    }

    private void rotation(Node<T> node, Side side) {
        if (node.getChild(side.opposite()) == null) return;

        Node<T> sideChild = node.getChild(side.opposite());
        node.setChild(sideChild.getChild(side), side.opposite());

        if (node.getParent() == null) setRoot(sideChild);
        else                          node.getParent().setChild(sideChild, node.getChildKind());

        sideChild.setChild(node, side);
    }

    private void setRoot(Node<T> root) {
        this.root = root;
        if (root != null) root.setRoot();
    }

    private static enum Side { 
        LEFT, RIGHT;

        public Side opposite() { return this == LEFT ? RIGHT : LEFT; }
    }

    private static class Node<T extends Comparable<T>> {
        private T value;
        private Node<T> left, right, parent;

        public Node(T value) { this(value, null, null, null); } 

        public Node(T value, Node<T> left, Node<T> right, Node<T> parent) {
            setValue (value );
            setLeft  (left  );
            setRight (right );
            setParent(parent);
        }

        public Node<T> setLeft(Node<T> left) {
            this.left = left;
            if (left != null) left.parent = this;
            return this;
        }

        public Node<T> setRight(Node<T> right) {
            this.right = right;
            if (right != null) right.parent = this;
            return this;
        }

        public Node<T> setChild(Node<T> child, Side side) { return side == Side.LEFT ? setLeft(child) : setRight(child); }

        public Node<T> setRoot() { return setParent(null); }

        private Node<T> setParent(Node<T> parent) {
            this.parent = parent;
            return this;
        }

        public Node<T> setValue(T value) {
            this.value = notNull(value);
            return this;
        }

        public boolean isRoot() { return parent == null; }

        public boolean isLeftChild () { return isRoot() || getParent().getValue().compareTo(getValue()) > 0; }
        public boolean isRightChild() { return isRoot() || !isLeftChild()                                  ; }

        public Node<T> getChild(Side side) { return side == Side.LEFT ? getLeft() : getRight(); }

        public Side getChildKind() { 
            Check.isFalse(isRoot(), "This method is not defined on root nodes");
            return isLeftChild() ? Side.LEFT : Side.RIGHT; 
        }

        public T       getValue () { return value ; }
        public Node<T> getLeft  () { return left  ; }
        public Node<T> getRight () { return right ; }
        public Node<T> getParent() { return parent; }
    }
}

注意:我的树并不总是以最佳方式重新平衡。我做了这件事,但是我会在维基百科看看他们说的话,我可能没有应用正确的算法,但它在病理案例中已经很好用。