1. 程式人生 > >Data Structure - Splay Tree (Java)

Data Structure - Splay Tree (Java)

package chimomo.learning.java.datastructure;

/**
 * Implements a top-down splay tree.
 * Note that all "matching" is based on the compareTo method.
 *
 * @author Created by Chimomo
 */
public class SplayTree<T extends Comparable<? super T>> {
    // Used between different inserts.
    private BinaryNode<T> newNode = null;

    // For splay.
    private BinaryNode<T> header = new BinaryNode<>(null);
    private BinaryNode<T> root;
    private BinaryNode<T> nullNode;

    /**
     * Construct the tree.
     */
    public SplayTree() {
        nullNode = new BinaryNode<>(null);
        nullNode.left = nullNode.right = nullNode;
        root = nullNode;
    }

    // Test program.
    // Should print min and max and nothing else.
    public static void main(String[] args) throws Exception {
        // Construct splay tree.
        SplayTree<Integer> t = new SplayTree<>();
        final int NUMS = 40000;
        final int GAP = 307;

        System.out.println("Checking... (no bad output means success)");

        // Insert.
        for (int i = GAP; i != 0; i = (i + GAP) % NUMS) {
            t.insert(i);
        }
        System.out.println("Inserts complete");

        // Remove.
        for (int i = 1; i < NUMS; i += 2) {
            t.remove(i);
        }
        System.out.println("Removes complete");

        // Find min and find max.
        if (t.findMin() != 2 || t.findMax() != NUMS - 2) {
            System.out.println("FindMin or FindMax error!");
        }

        // Contains.
        for (int i = 2; i < NUMS; i += 2) {
            if (!t.contains(i)) {
                System.out.println("Error: Find fails for " + i);
            }
        }
        for (int i = 1; i < NUMS; i += 2) {
            if (t.contains(i)) {
                System.out.println("Error: Found deleted item " + i);
            }
        }
    }

    /**
     * Rotate binary tree node with left child.
     * For AVL trees, this is a single rotation for case 1.
     */
    private static <AnyType> BinaryNode<AnyType> rotateWithLeftChild(BinaryNode<AnyType> k2) {
        BinaryNode<AnyType> k1 = k2.left;
        k2.left = k1.right;
        k1.right = k2;
        return k1;
    }

    /**
     * Rotate binary tree node with right child.
     * For AVL trees, this is a single rotation for case 4.
     */
    private static <AnyType> BinaryNode<AnyType> rotateWithRightChild(BinaryNode<AnyType> k1) {
        BinaryNode<AnyType> k2 = k1.right;
        k1.right = k2.left;
        k2.left = k1;
        return k2;
    }

    /**
     * Insert into the tree.
     *
     * @param x The item to insert.
     */
    public void insert(T x) {
        if (newNode == null) {
            newNode = new BinaryNode<>(null);
        }
        newNode.element = x;

        if (root == nullNode) {
            newNode.left = newNode.right = nullNode;
            root = newNode;
        } else {
            root = splay(x, root);

            int compareResult = x.compareTo(root.element);
            if (compareResult < 0) {
                newNode.left = root.left;
                newNode.right = root;
                root.left = nullNode;
                root = newNode;
            } else if (compareResult > 0) {
                newNode.right = root.right;
                newNode.left = root;
                root.right = nullNode;
                root = newNode;
            } else {
                // No duplicates.
                return;
            }
        }

        // So next insert will call new.
        newNode = null;
    }

    /**
     * Remove from the tree.
     *
     * @param x The item to remove.
     */
    public void remove(T x) {
        if (!contains(x)) {
            return;
        }

        BinaryNode<T> newTree;

        // If x is found, it will be splayed to the root by contains.
        if (root.left == nullNode) {
            newTree = root.right;
        } else {
            // Find the maximum in the left subtree.
            // Splay it to the root; and then attach right child.
            newTree = root.left;
            newTree = splay(x, newTree);
            newTree.right = root.right;
        }
        root = newTree;
    }

    /**
     * Find the smallest item in the tree.
     * Not the most efficient implementation (uses two passes), but has correct amortized behavior.
     * A good alternative is to first call find with parameter smaller than any item in the tree, then call findMin.
     *
     * @return The smallest item or throw exception if empty.
     */
    public T findMin() throws Exception {
        if (isEmpty()) {
            throw new Exception("Splay tree is empty!");
        }

        BinaryNode<T> ptr = root;
        while (ptr.left != nullNode) {
            ptr = ptr.left;
        }

        root = splay(ptr.element, root);
        return ptr.element;
    }

    /**
     * Find the largest item in the tree.
     * Not the most efficient implementation (uses two passes), but has correct amortized behavior.
     * A good alternative is to first call find with parameter larger than any item in the tree, then call findMax.
     *
     * @return The largest item or throw exception if empty.
     */
    public T findMax() throws Exception {
        if (isEmpty()) {
            throw new Exception("Splay tree is empty!");
        }

        BinaryNode<T> ptr = root;
        while (ptr.right != nullNode) {
            ptr = ptr.right;
        }

        root = splay(ptr.element, root);
        return ptr.element;
    }

    /**
     * Find an item in the tree.
     *
     * @param x The item to search for.
     * @return True if x is found; false otherwise.
     */
    public boolean contains(T x) {
        if (isEmpty()) {
            return false;
        }

        root = splay(x, root);
        return root.element.compareTo(x) == 0;
    }

    /**
     * Make the tree logically empty.
     */
    public void makeEmpty() {
        root = nullNode;
    }

    /**
     * Test if the tree is logically empty.
     *
     * @return True if empty, false otherwise.
     */
    public boolean isEmpty() {
        return root == nullNode;
    }

    /**
     * Internal method to perform a top-down splay.
     * The last accessed node becomes the new root.
     *
     * @param x The target item to splay around.
     * @param t The root of the subtree to splay.
     * @return The subtree after the splay.
     */
    private BinaryNode<T> splay(T x, BinaryNode<T> t) {
        BinaryNode<T> leftTree, rightTree;
        header.left = header.right = nullNode;
        leftTree = rightTree = header;

        // Guarantee a match.
        nullNode.element = x;
        for (; ; ) {
            int compareResult = x.compareTo(t.element);
            if (compareResult < 0) {
                if (x.compareTo(t.left.element) < 0) {
                    t = rotateWithLeftChild(t);
                }
                if (t.left == nullNode) {
                    break;
                }

                // Link right.
                rightTree.left = t;
                rightTree = t;
                t = t.left;
            } else if (compareResult > 0) {
                if (x.compareTo(t.right.element) > 0) {
                    t = rotateWithRightChild(t);
                }
                if (t.right == nullNode) {
                    break;
                }

                // Link left.
                leftTree.right = t;
                leftTree = t;
                t = t.right;
            } else {
                break;
            }
        }

        leftTree.right = t.left;
        rightTree.left = t.right;
        t.left = header.right;
        t.right = header.left;
        return t;
    }

    /**
     * Basic node stored in unbalanced binary search trees.
     *
     * @param <AnyType> Any type.
     */
    private static class BinaryNode<AnyType> {
        AnyType element;            // The data in the node.
        BinaryNode<AnyType> left;   // Left child.
        BinaryNode<AnyType> right;  // Right child.

        // Constructors.
        BinaryNode(AnyType element) {
            this(element, null, null);
        }

        BinaryNode(AnyType element, BinaryNode<AnyType> left, BinaryNode<AnyType> right) {
            this.element = element;
            this.left = left;
            this.right = right;
        }
    }
}

/*
Output:
Checking... (no bad output means success)
Inserts complete
Removes complete

*/