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

Data Structure - AVL Tree (Java)

package chimomo.learning.java.datastructure;

/**
 * Implements an AVL tree.
 * Note that all "matching" is based on the compareTo method.
 *
 * @author Created by Chimomo
 */
public class AvlTree<T extends Comparable<? super T>> {
    private static final int ALLOWED_IMBALANCE = 1;

    // The tree root.
    private AvlNode<T> root;

    /**
     * Construct the tree.
     */
    public AvlTree() {
        root = null;
    }

    // Test program.
    public static void main(String[] args) throws Exception {
        // Construct AVL tree.
        AvlTree<Integer> t = new AvlTree<>();
        final int SMALL = 40;
        final int NUMS = 1000000; // must be even
        final int GAP = 37;

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

        // Insert.
        for (int i = GAP; i != 0; i = (i + GAP) % NUMS) {
            t.insert(i);
            if (NUMS < SMALL) {
                t.checkBalance();
            }
        }

        // Remove.
        for (int i = 1; i < NUMS; i += 2) {
            t.remove(i);
            if (NUMS < SMALL) {
                t.checkBalance();
            }
        }

        // Print tree.
        if (NUMS < SMALL) {
            t.printTree();
        }
        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("Find error1!");
            }
        for (int i = 1; i < NUMS; i += 2) {
            if (t.contains(i)) {
                System.out.println("Find error2!");
            }
        }
    }

    /**
     * Insert into the tree.
     * Duplicates are ignored.
     *
     * @param x The item to insert.
     */
    public void insert(T x) {
        root = insert(x, root);
    }

    /**
     * Remove from the tree.
     * Nothing is done if x is not found.
     *
     * @param x The item to remove.
     */
    public void remove(T x) {
        root = remove(x, root);
    }

    /**
     * Internal method to remove from the subtree.
     *
     * @param x The item to remove.
     * @param t The node that roots the subtree.
     * @return The new root of the subtree.
     */
    private AvlNode<T> remove(T x, AvlNode<T> t) {
        // Item not found; do nothing
        if (t == null) {
            return t;
        }

        int compareResult = x.compareTo(t.element);
        if (compareResult < 0) {
            t.left = remove(x, t.left);
        } else if (compareResult > 0) {
            t.right = remove(x, t.right);
        } else if (t.left != null && t.right != null) { // Two children
            t.element = findMin(t.right).element;
            t.right = remove(t.element, t.right);
        } else {
            t = (t.left != null) ? t.left : t.right;
        }

        return balance(t);
    }

    /**
     * Find the smallest item in the tree.
     *
     * @return The smallest item or null if empty.
     */
    public T findMin() throws Exception {
        if (isEmpty()) {
            throw new Exception("The AVL tree is empty!");
        }
        return findMin(root).element;
    }

    /**
     * Find the largest item in the tree.
     *
     * @return The largest item of null if empty.
     */
    public T findMax() throws Exception {
        if (isEmpty()) {
            throw new Exception("The AVL tree is empty!");
        }
        return findMax(root).element;
    }

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

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

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

    /**
     * Print the tree contents in sorted order.
     */
    public void printTree() {
        if (isEmpty()) {
            System.out.println("The AVL tree is empty!");
        } else {
            printTree(root);
        }
    }

    /**
     * Balance the tree.
     * Assume the tree is either balanced or within one of being balanced.
     *
     * @param t The tree root node.
     * @return The balanced tree.
     */
    private AvlNode<T> balance(AvlNode<T> t) {
        if (t == null) {
            return t;
        }

        if (height(t.left) - height(t.right) > ALLOWED_IMBALANCE) {
            if (height(t.left.left) >= height(t.left.right)) {
                t = rotateWithLeftChild(t);
            } else {
                t = doubleRotateWithLeftChild(t);
            }
        } else if (height(t.right) - height(t.left) > ALLOWED_IMBALANCE) {
            if (height(t.right.right) >= height(t.right.left)) {
                t = rotateWithRightChild(t);
            } else {
                t = doubleRotateWithRightChild(t);
            }
        }

        t.height = Math.max(height(t.left), height(t.right)) + 1;
        return t;
    }

    /**
     * Check balance.
     */
    public void checkBalance() {
        checkBalance(root);
    }

    /**
     * Check balance.
     *
     * @param t The tree root.
     * @return The tree height.
     */
    private int checkBalance(AvlNode<T> t) {
        if (t == null) {
            return -1;
        }

        if (t != null) {
            int leftHeight = checkBalance(t.left);
            int rightHeight = checkBalance(t.right);
            if (Math.abs(height(t.left) - height(t.right)) > 1
                    || height(t.left) != leftHeight
                    || height(t.right) != rightHeight) {
                System.out.println("OOPS!!");
            }
        }

        return height(t);
    }

    /**
     * Internal method to insert into the subtree.
     *
     * @param x The item to insert.
     * @param t The node that roots the subtree.
     * @return The new root of the subtree.
     */
    private AvlNode<T> insert(T x, AvlNode<T> t) {
        if (t == null) {
            return new AvlNode<>(x, null, null);
        }

        int compareResult = x.compareTo(t.element);
        if (compareResult < 0) {
            t.left = insert(x, t.left);
        } else if (compareResult > 0) {
            t.right = insert(x, t.right);
        } else { // Duplicate; do nothing
        }

        return balance(t);
    }

    /**
     * Internal method to find the smallest item in the subtree.
     *
     * @param t The node that roots the tree.
     * @return The node containing the smallest item.
     */
    private AvlNode<T> findMin(AvlNode<T> t) {
        if (t == null) {
            return t;
        }

        while (t.left != null) {
            t = t.left;
        }
        return t;
    }

    /**
     * Internal method to find the largest item in the subtree.
     *
     * @param t The node that roots the tree.
     * @return The node containing the largest item.
     */
    private AvlNode<T> findMax(AvlNode<T> t) {
        if (t == null) {
            return t;
        }

        while (t.right != null) {
            t = t.right;
        }
        return t;
    }

    /**
     * Internal method to find an item in the subtree.
     *
     * @param x The item to search for.
     * @param t The node that roots the tree.
     * @return True if x is found in subtree.
     */
    private boolean contains(T x, AvlNode<T> t) {
        while (t != null) {
            int compareResult = x.compareTo(t.element);
            if (compareResult < 0) {
                t = t.left;
            } else if (compareResult > 0) {
                t = t.right;
            } else { // Match.
                return true;
            }
        }

        return false; // No match.
    }

    /**
     * Internal method to print the subtree in sorted order.
     *
     * @param t The node that roots the tree.
     */
    private void printTree(AvlNode<T> t) {
        if (t != null) {
            printTree(t.left);
            System.out.println(t.element);
            printTree(t.right);
        }
    }

    /**
     * Return the height of node t, or -1, if null.
     *
     * @param t The node.
     * @return The height of node t, or -1, if null.
     */
    private int height(AvlNode<T> t) {
        return t == null ? -1 : t.height;
    }

    /**
     * Rotate binary tree node with left child.
     * For AVL trees, this is a single rotation for case 1.
     * Update heights, then return new root.
     */
    private AvlNode<T> rotateWithLeftChild(AvlNode<T> k2) {
        AvlNode<T> k1 = k2.left;
        k2.left = k1.right;
        k1.right = k2;
        k2.height = Math.max(height(k2.left), height(k2.right)) + 1;
        k1.height = Math.max(height(k1.left), k2.height) + 1;
        return k1;
    }

    /**
     * Rotate binary tree node with right child.
     * For AVL trees, this is a single rotation for case 4.
     * Update heights, then return new root.
     */
    private AvlNode<T> rotateWithRightChild(AvlNode<T> k1) {
        AvlNode<T> k2 = k1.right;
        k1.right = k2.left;
        k2.left = k1;
        k1.height = Math.max(height(k1.left), height(k1.right)) + 1;
        k2.height = Math.max(height(k2.right), k1.height) + 1;
        return k2;
    }

    /**
     * Double rotate binary tree node:
     * First left child with its right child; then node k3 with new left child.
     * For AVL trees, this is a double rotation for case 2.
     * Update heights, then return new root.
     */
    private AvlNode<T> doubleRotateWithLeftChild(AvlNode<T> k3) {
        k3.left = rotateWithRightChild(k3.left);
        return rotateWithLeftChild(k3);
    }

    /**
     * Double rotate binary tree node:
     * First right child with its left child; then node k1 with new right child.
     * For AVL trees, this is a double rotation for case 3.
     * Update heights, then return new root.
     */
    private AvlNode<T> doubleRotateWithRightChild(AvlNode<T> k1) {
        k1.right = rotateWithLeftChild(k1.right);
        return rotateWithRightChild(k1);
    }

    /**
     * The AVL node.
     *
     * @param <T> Any type.
     */
    private static class AvlNode<T> {
        T element; // The data in the node.
        AvlNode<T> left; // Left child.
        AvlNode<T> right; // Right child.
        int height; // Height.

        // Constructors.
        AvlNode(T theElement) {
            this(theElement, null, null);
        }

        AvlNode(T element, AvlNode<T> left, AvlNode<T> right) {
            this.element = element;
            this.left = left;
            this.right = right;
            this.height = 0;
        }
    }
}

/*
Output:
Checking... (no more output means success)

*/