简体   繁体   中英

Implementing splay() method for Binary Search Tree

I am trying to implement a splay(Node x) method for a Binary Search Tree. I have the leftRotation(Node x) and rightRotation(Node x) methods implemented correctly (atleast, I think they are...), but when I try to implement them in a splay(Node x) method, it calls itself in an infinite loop. Now, I know why it's doing that, but can't seem to figure out how to fix it.

Here is the leftRotation(Node x) method:

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);
}

Here's the rightRotation(Node x) method:

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);
}

And here's the splay(Node x) method:

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);
        }
    }
}

Any ideas on how to fix the infinite loop? It seems to be something to do with it not breaking out of the while(x.getParent() != null) statement in the splay(Node x) method, but when I went through the code using the debugger, the properties of the node seemed to be changing, so I don't really know where it's going wrong?

setLeftChild(Node leftChild) method:

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

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

Apart from all the mistakes/bad things I pointed out in your code, here is the biggest one, in rightRotation :

x.setRightChild(x);

This creates a cycle in your tree, hence the infinite loop. You should have unit tested your methods. One other major error in your code is that your if - else if instructions do not have an else so there might be cases where nothing happens during an iteration... hence the infinite loop. It's not the case here because you considered all the cases (actually, you considered even more, and the two last ones will never be executed because the four first cases cover every possible case) but as a general remark, this was really dangerous to code it that way.

Here is the code of my own implementation of all these methods, which I deem cleaner :

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; }
    }
}

Note : my tree is not always optimally rebalanced. I did this out of my head but I will check at Wikipedia to see what they say, I probably did not apply the right algorithm, but it works pretty fine already expect in pathologic cases.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM