AVL樹旋轉及代碼實現

    AVL樹是帶有平衡條件的二叉查找樹,它允許每個節點的左子樹與右子樹的高度差未1。這樣的平衡樹深度是O(log N)。而要維持這種平衡,就必須在每次對AVL樹刪除節點或新增節點後,檢查AVL樹的平衡是否被打破,即是否存在節點的左右子樹高度差大於1,在判斷AVL樹失去平衡後,就必須旋轉失去平衡的節點,已再次達到平衡。

AVL樹旋轉:

    旋轉的目的就是減小樹的高度,節點未空時,節點的高度未-1;

    1.單旋轉:

    右旋:
   
    當在節點3的插入節點2前,節點5的高度爲1,左節點3的高度爲0,插入節點2後,節點5左節點3的高度爲1,而右節點高度爲-1,兩者高度差等於2,節點5失去平衡。
    所以已節點3爲支點,將節點5右旋至節點3的右節點上。
    旋轉完成後,AVL樹的高度降低,需重新計算每個節點的高度(遞歸計算)。

    左旋:

    

    計算節點高度同上,計算後節點2失去平衡,將節點2旋轉到其右節點 3的左節點上,在遞歸計算AVL樹每個節點的高度。

    2.雙旋轉:

    先左旋再右旋:

   

    如同所示,在節點3上插入節點4後,節點5失去平衡,如果旋轉恢復平衡則要先將節點3旋轉至節點4的左節點上,再Jiangxi節點5旋轉至節點4的右節點上,通過左旋+右旋兩次旋轉恢復AVL樹的平衡,並重新計算節點高度。

    先右旋再左旋:

   

    如圖所示,在節點5插入節點4後,AVL樹在節點3處失去平衡,因此先將節點5右旋至節點4的右節點上,在講節點3旋轉至節點4的左節點上,通過右旋+左旋兩次旋轉恢復AVL樹的平衡,並重新計算節點高度。


AVL樹的添加及刪除操作代碼如下:

package com.ybin.btree;

/**
 * @author yuebing
 * @version 1.0
 * @Date 2018/5/11
 * @category AVL樹操作
 */
public class AvlTree<E extends Comparable> {

    /**
     * 根節點
     */
    private Node root;
    /**
     * AVL樹高度
     */
    private int height;

    private static final class Node<E> {
        /**
         * 元素值
         */
        E element;
        /**
         * 左節點
         */
        Node left;
        /**
         * 右節點
         */
        Node right;
        /**
         * 節點高度
         */
        int nodeHeight;

        public Node(E element) {
            this(element, null, null);
        }

        public Node(E element, Node left, Node right) {
            this.element = element;
            this.left = left;
            this.right = right;
        }
    }

    public void add(E e) {
        if (e != null)
            this.root = insert(e, root);
    }

    private Node insert (E e, Node node) {
        if (node == null) {
            return new Node(e);
        }
        int compare = e.compareTo(node.element);
        if (compare < 0) {
            node.left = insert(e, node.left);
        }else if (compare > 0) {
            node.right = insert(e, node.right);
        } else {
            return node;
        }
        return balance(node);
    }

    public void remove(E e) {
        if (e != null) {
            this.root = this.remove(e, root);
        }
    }

    private Node remove(E e, Node node) {
        if (node == null) {
            return node;
        }
        int compare = e.compareTo(node.element);
        if (compare < 0) {
            node.left = remove(e, node.left);
        } else if (compare > 0) {
            node.right = remove(e, node.right);
        } else if (node.left != null && node.right != null) {
            //找出該節點右節點的最小節點,替換被刪除的節點
            Node min = node.right;
            while (min.left != null) {
                min = min.left;
            }
            node.element = min.element;
            node.right = remove((E) node.element, node.right);
        } else {
            node = node.left == null ? node.right : node.left;
        }
        return this.balance(node);
    }

    /**
     * 添加節點後,平衡AVL樹
     *
     * @param node
     * @return
     */
    private Node balance(Node node) {
        if (node == null) {
            return node;
        }
        if (height(node.left) - height(node.right) > 1) {
            if (height(node.left.left) >= height(node.left.right)) {
                node = this.rightRotate(node);
            } else {
                node = this.leftAndRightRotate(node);
            }
        } else if (height(node.right) - height(node.left) > 1) {
            if (height(node.right.right) >= height(node.right.left)) {
                node = this.leftRotate(node);
            } else {
                node = this.rightAndLeftRotate(node);
            }

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

    /**
     * 先左旋再右旋
     * 1.先將該節點的左孩子節點左旋;
     * 2.再將旋轉後的節點,右旋;
     * 3.重新計算旋轉後各節點的高度;
     * 4.旋轉效果:
     *       3      |      3    |     2
     *      /       |     /     |    / \
     *     1        ->   2      ->  1  3
     *      \       |   /       |
     *       2      |  1        |
     * @param node
     * @return
     */
    private Node leftAndRightRotate(Node node) {
        node.left = this.leftRotate(node.left);
        return rightRotate(node);
    }

    /**
     * 先右旋再左旋
     * 1.先將該節點的右孩子節點右旋;
     * 2.先將上一步旋轉完成的節點,左旋;
     * 3.再重新計算旋轉後各節點的高度;
     * 4.旋轉效果:
     *     1      |    1      |     2
     *      \     |     \     |    / \
     *       3    ->    2     ->  1  3
     *      /     |      \    |
     *     2      |       3   |
     * @param node
     * @return
     */
    private Node rightAndLeftRotate(Node node) {
        node.right = this.rightRotate(node.right);
        return leftRotate(node);
    }

    /**
     * 左旋
     * 1.以傳入的節點的右節點爲支點,將傳入的節點旋轉到該支點節點的左節點上;
     * 2.重新計算旋轉後的節點的高度;
     * 3.旋轉效果:
     *      1       |        2
     *       \      |       / \
     *        2     ->     1  3
     *         \    |
     *          3   |
     * @param oldNode
     * @return
     */
    private Node leftRotate(Node oldNode) {
        Node  newNode = oldNode.right;
        oldNode.right = newNode.left;
        newNode.left = oldNode;
        oldNode.nodeHeight = Math.max(height(oldNode.left), height(oldNode.right)) + 1;
        newNode.nodeHeight = Math.max(height(newNode.right), oldNode.nodeHeight) + 1;
        return newNode;
    }

    /**
     * 右旋
     * 1.以傳入的節點的左節點爲支點,將傳入的節點旋轉到該支點節點的右節點上;
     * 2.重新計算旋轉後的節點的高度;
     * 3.旋轉效果:
     *          3    |       2
     *         /     |      / \
     *        2      ->    1   3
     *       /       |
     *      1        |
     * @param oldNode
     * @return
     */
    private Node rightRotate(Node oldNode) {
        Node newNode = oldNode.left;
        oldNode.left = newNode.right;
        newNode.right = oldNode;
        oldNode.nodeHeight = Math.max(height(oldNode.left), height(oldNode.right)) + 1;
        newNode.nodeHeight = Math.max(height(newNode.left), oldNode.nodeHeight) + 1;
        return newNode;
    }

    /**
     * 獲取節點高度
     * 1.節點爲空,則高度爲-1;
     * 2.否則返回節點高度;
     *
     * @param node
     * @return
     */
    private int height(Node node) {
        return node == null ? -1 : node.nodeHeight;
    }
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章