AVL平衡二叉树

定义

一棵AVL树是每个节点的左子树和右子树的高度最多差1的二叉查找树(空树高度定义为-1)

旋转

1.单旋转

AVL树本质是一棵二叉查找树,所以AVL树的插入和删除其实和二叉查找树一致,只是每次插入和删除后要做平衡性调整。AVL树比较难的地方就是旋转维持树的平衡性。先以右旋为例,分析一下如何进行右旋(左旋是对称操作)。

/**
     * Rotates the subtree so that its root's left child is the new root.
     */
    private void rotateRight(Node<V> Y) {
        Node<V> X = Y.left;
        Node<V> right = Y.right;
        Node<V> α = X.left;
        Node<V> β = X.right;

        //第一步:将当前节点的左孩子修改为其之前左孩子的右孩子,同时修改parent指针
        Y.left = β;
        if (β != null) {
            β.parent = Y;
        }
        //第二步:将当前节点左孩子的parent修改为当前节点的parent
        replaceInParent(Y, X);

        //第三步:将左孩子的右节点指向自己,并将自己的parent指向左孩子
        X.right = Y;
        Y.parent = X;

        //第四步:更新节点的高度
        Y.height = Math.max(right != null ? right.height : 0,
                β != null ? β.height : 0) + 1;
        X.height = Math.max(Y.height,
                α != null ? α.height : 0) + 1;
    }

2.双旋转

双旋转是单旋转的组合操作,一些情形下一次单旋转并不能平衡,所以需要一次双循环。假设在下图中的B或C处的一次插入操作导致K3处两棵子树高度差为2。那插入节点后应该是B或C的深度比D高2,AB或C不可能在同一层上,否则插入之前就已经不平衡了。AD也不可能在同一层上,如果AD在同一层,那么K1就是第一个导致不平衡的节点了。

假设我们只对K3进行一次右旋转,则会变成如下效果:

由于B或C的深度没有变,而A的深度少了1,由于之前分析的一样,A之前的深度就已经比插入后的B或C深度小,因此旋转并没有平衡。针对这种情况,我们一次需要双旋转,只能让K2做根,迫使K1K2的左孩子。因此,首先针对K1进行一次左旋。然后再对K2进行右旋。
rotateLeft(root.left) //左旋K1

rotateRight(root) //左旋K3

旋转判断条件

假设添加节点后,节点α是沿着当前节点向根查找发现的第一个破坏平衡条件的点,针对α点不平衡可能出现下面四种情况和处理方式:

  1. 对α的左儿子的左子树进行了一次插入 //rorateRight(α)
  2. 对α的左儿子的右子树进行了一次插入 //rorateLeft(α.left) ,rorateRight(α)
  3. 对α的右儿子的左子树进行了一次插入 //rorateRight(α.right) ,rorateLeft(α)
  4. 对α的右儿子的右子树进行了一次插入 //rorateLeft(α)

代码实现

Java源码中TreeMap是一个标准的AVL树实现,因此将源码做了简单修改,总结AVL树的操作(现在的TreeMap源码已经改为红黑树实现)

/**
 * Created by 默默 on 2018/3/6.
 */
public class AVLTree<V extends Comparable<? super V>> {
    Node<V> root;
    int size = 0;
    int modCount = 0;

    public AVLTree() {
    }

    public int size() {
        return size;
    }

    public boolean isEmpty() {
        return size == 0;
    }

    public Node<V> insert(V value) {
        if (root == null) {
            root = new Node<V>(null, value);
            size = 1;
            modCount++;
            return root;
        }
        Node<V> nearest = root;
        while (true) {
            int comparison = value.compareTo(nearest.value);
                /*
                 * We found the requested value.
                 */
            if (comparison == 0) {
                return nearest;
            }

            Node<V> child = (comparison < 0) ? nearest.left : nearest.right;
            if (child != null) {
                nearest = child;
                continue;
            }

            if (comparison < 0) { // nearest value is higher
                Node<V> created = new Node<>(nearest, value);
                nearest.left = created;
                size++;
                modCount++;
                rebalance(nearest, true);
                return created;
            } else { // comparison > 0, nearest value is lower
                Node<V> created = new Node<>(nearest, value);
                nearest.right = created;
                size++;
                modCount++;
                rebalance(nearest, true);
                return created;
            }
        }
    }

    public void clear() {
        root = null;
        size = 0;
        modCount++;
    }

    public V remove(V value) {
        Node<V> node = find(value);
        if (node != null) {
            removeInternal(node);
        }
        return node != null ? node.value : null;
    }

    public Node<V> find(V value) {
        if (root == null) {
            return null;
        }
        Node<V> nearest = root;
        while (true) {
            int comparison = value.compareTo(nearest.value);
                /*
                 * We found the requested key.
                 */
            if (comparison == 0) {
                return nearest;
            }

            Node<V> child = (comparison < 0) ? nearest.left : nearest.right;
            if (child != null) {
                nearest = child;
                continue;
            }
                /*
                 * We found a nearest node. Every value not in the tree has up to two
                 * nearest nodes, one lower and one higher.
                 */
            if (comparison < 0) { // nearest value is higher
                return null;
            } else { // comparison > 0, nearest value is lower
                return null;
            }
        }
    }

    /**
     * Removes {@code node} from this tree, rearranging the tree's structure as
     * necessary.
     */
    void removeInternal(Node<V> node) {
        Node<V> left = node.left;
        Node<V> right = node.right;
        Node<V> originalParent = node.parent;
        if (left != null && right != null) {
                /*
                 * To remove a node with both left and right subtrees, move an
                 * adjacent node from one of those subtrees into this node's place.
                 *
                 * Removing the adjacent node may change this node's subtrees. This
                 * node may no longer have two subtrees once the adjacent node is
                 * gone!
                 */

            Node<V> adjacent = (left.height > right.height) ? left.last() : right.first();
            removeInternal(adjacent); // takes care of rebalance and size--

            int leftHeight = 0;
            left = node.left;
            if (left != null) {
                leftHeight = left.height;
                adjacent.left = left;
                left.parent = adjacent;
                node.left = null;
            }
            int rightHeight = 0;
            right = node.right;
            if (right != null) {
                rightHeight = right.height;
                adjacent.right = right;
                right.parent = adjacent;
                node.right = null;
            }
            adjacent.height = Math.max(leftHeight, rightHeight) + 1;
            replaceInParent(node, adjacent);
            return;
        } else if (left != null) {
            replaceInParent(node, left);
            node.left = null;
        } else if (right != null) {
            replaceInParent(node, right);
            node.right = null;
        } else {
            replaceInParent(node, null);
        }

        rebalance(originalParent, false);
        size--;
        modCount++;
    }

    private void replaceInParent(Node<V> node, Node<V> replacement) {
        Node<V> parent = node.parent;
        node.parent = null;
        if (replacement != null) {
            replacement.parent = parent;
        }

        if (parent != null) {
            if (parent.left == node) {
                parent.left = replacement;
            } else {
                assert (parent.right == node);
                parent.right = replacement;
            }
        } else {
            root = replacement;
        }
    }

    /**
     * Rebalances the tree by making any AVL rotations necessary between the
     * newly-unbalanced node and the tree's root.
     *
     * @param insert true if the node was unbalanced by an insert; false if it
     *               was by a removal.
     */
    private void rebalance(Node<V> unbalanced, boolean insert) {
        for (Node<V> node = unbalanced; node != null; node = node.parent) {
            Node<V> left = node.left;
            Node<V> right = node.right;
            int leftHeight = left != null ? left.height : 0;
            int rightHeight = right != null ? right.height : 0;

            int delta = leftHeight - rightHeight;
            if (delta == -2) {
                Node<V> rightLeft = right.left;
                Node<V> rightRight = right.right;
                int rightRightHeight = rightRight != null ? rightRight.height : 0;
                int rightLeftHeight = rightLeft != null ? rightLeft.height : 0;

                int rightDelta = rightLeftHeight - rightRightHeight;
                if (rightDelta == -1 || (rightDelta == 0 && !insert)) {
                    rotateLeft(node); // AVL right right
                } else {
                    //assert (rightDelta == 1);
                    rotateRight(right); // AVL right left
                    rotateLeft(node);
                }
                if (insert) {
                    break; // no further rotations will be necessary
                }

            } else if (delta == 2) {
                Node<V> leftLeft = left.left;
                Node<V> leftRight = left.right;
                int leftRightHeight = leftRight != null ? leftRight.height : 0;
                int leftLeftHeight = leftLeft != null ? leftLeft.height : 0;

                int leftDelta = leftLeftHeight - leftRightHeight;
                if (leftDelta == 1 || (leftDelta == 0 && !insert)) {
                    rotateRight(node); // AVL left left
                } else {
                    //assert (leftDelta == -1);
                    rotateLeft(left); // AVL left right
                    rotateRight(node);
                }
                if (insert) {
                    break; // no further rotations will be necessary
                }

            } else if (delta == 0) {

                //删除一个节点后,高度一致,需要更新parent所有节点高度并判断是否产生不平衡
                node.height = leftHeight + 1; // leftHeight == rightHeight
                if (insert) {
                    //如果是插入一个节点,节点高度没有发生变化,不需要再往上更新,因为节点高度没有发生变化
                    break; 
                }

            } else {
                //assert (delta == -1 || delta == 1);
                //插入一个节点后,高度发生变化,需要更新parent所有节点高度并判断是否发生了不平衡
                node.height = Math.max(leftHeight, rightHeight) + 1;
                if (!insert) {
                    //如果删除一个节点,高度差一,说明之前高度一致,不需要再往上更新,因为删除后各个节点高度没有发生变化
                    break; 
                }
            }
        }
    }

    /**
     * Rotates the subtree so that its root's right child is the new root.
     */
    private void rotateLeft(Node<V> root) {
        Node<V> left = root.left;
        Node<V> pivot = root.right;
        Node<V> pivotLeft = pivot.left;
        Node<V> pivotRight = pivot.right;

        // move the pivot's left child to the root's right
        root.right = pivotLeft;
        if (pivotLeft != null) {
            pivotLeft.parent = root;
        }

        replaceInParent(root, pivot);

        // move the root to the pivot's left
        pivot.left = root;
        root.parent = pivot;

        // fix heights
        root.height = Math.max(left != null ? left.height : 0,
                pivotLeft != null ? pivotLeft.height : 0) + 1;
        pivot.height = Math.max(root.height,
                pivotRight != null ? pivotRight.height : 0) + 1;
    }

    /**
     * Rotates the subtree so that its root's left child is the new root.
     */
    private void rotateRight(Node<V> root) {
        Node<V> pivot = root.left;
        Node<V> right = root.right;
        Node<V> pivotLeft = pivot.left;
        Node<V> pivotRight = pivot.right;

        // move the pivot's right child to the root's left
        root.left = pivotRight;
        if (pivotRight != null) {
            pivotRight.parent = root;
        }

        replaceInParent(root, pivot);

        // move the root to the pivot's right
        pivot.right = root;
        root.parent = pivot;

        // fixup heights
        root.height = Math.max(right != null ? right.height : 0,
                pivotRight != null ? pivotRight.height : 0) + 1;
        pivot.height = Math.max(root.height,
                pivotLeft != null ? pivotLeft.height : 0) + 1;
    }

    static class Node<V> {
        Node<V> parent;
        Node<V> left;
        Node<V> right;
        V value;
        int height;

        Node(Node<V> parent, V value) {
            this.parent = parent;
            this.value = value;
            this.height = 1;
        }

        public V getValue() {
            return value;
        }

        public V setValue(V value) {
            V oldValue = this.value;
            this.value = value;
            return oldValue;
        }

        /**
         * Returns the next node in an inorder traversal, or null if this is the
         * last node in the tree.
         */
        Node<V> next() {
            if (right != null) {
                return right.first();
            }

            Node<V> node = this;
            Node<V> parent = node.parent;
            while (parent != null) {
                if (parent.left == node) {
                    return parent;
                }
                node = parent;
                parent = node.parent;
            }
            return null;
        }

        /**
         * Returns the previous node in an inorder traversal, or null if this is
         * the first node in the tree.
         */
        public Node<V> prev() {
            if (left != null) {
                return left.last();
            }

            Node<V> node = this;
            Node<V> parent = node.parent;
            while (parent != null) {
                if (parent.right == node) {
                    return parent;
                }
                node = parent;
                parent = node.parent;
            }
            return null;
        }

        /**
         * Returns the first node in this subtree.
         */
        public Node<V> first() {
            Node<V> node = this;
            Node<V> child = node.left;
            while (child != null) {
                node = child;
                child = node.left;
            }
            return node;
        }

        /**
         * Returns the last node in this subtree.
         */
        public Node<V> last() {
            Node<V> node = this;
            Node<V> child = node.right;
            while (child != null) {
                node = child;
                child = node.right;
            }
            return node;
        }
    }

    public void preOrder() {
        if (root==null) {
            return;
        }
        System.out.println(root.value);
        preOrder(root.left);
        preOrder(root.right);
    }

    public static void main(String[] args) {
        AVLTree<Integer> tree =new AVLTree<>();
        int[] a= {1,2,3,4,5,6,7,16,15,14,13,12,11,10,8,9};
        /**
         *              7 
         *            /   \
         *         /        \
         *       4            13
         *     /   \       /     \
         *   2      6    11       15
         *  / \    /    /  \     /  \
         * 1   3  5    9    12  14  16   
         *            / \
         *           8   10 
         */
        for (int i : a) {
            tree.insert(i);
        }
        tree.preOrder();
    }
}

猜你喜欢

转载自blog.csdn.net/momo_ibeike/article/details/79455051