1. 程式人生 > >AVL樹Java實現

AVL樹Java實現

歡迎探討,如有錯誤敬請指正

如需轉載,請註明出處http://www.cnblogs.com/nullzx/

1. AVL定義

AVL樹是一種改進版的搜尋二叉樹。對於一般的搜尋二叉樹而言,如果資料恰好是按照從小到大的順序或者從大到小的順序插入的,那麼搜尋二叉樹就對退化成連結串列,這個時候查詢,插入和刪除的時間都會上升到O(n),而這對於海量資料而言,是我們無法忍受的。即使是一顆由完全隨機的資料構造成的搜尋二叉樹,從統計角度去分析,在進行若甘次的插入和刪除操作,這個搜尋二叉樹的高度也不能令人滿意。這個時候大家就希望能有一種二叉樹解決上述問題。這個時候就出現平衡搜尋二叉樹,它的基本原理就是在插入和刪除的時候,根據情況進行調整,以降低二叉樹的高度。平衡搜尋二叉樹典型代表就是AVL樹和紅黑樹。

AVL樹:任何一個節點的左子支高度與右子支高度之差的絕對值不超過1。需要我們注意的是,AVL樹定義不是說從根節點到葉子節點的最短距離比最長短距離大1。

image

上圖就是一顆AVL樹,從根節點到葉子節點的最短距離是5,最長距離是9。

2. 旋轉的定義

因為每種書中對旋轉的定義不一致,所以我們有必要在這裡特此說明一下

以某一個節點為軸,它的左子枝順時針旋轉,作為新子樹的根,我們稱之為順時針旋轉(clockwise)或者右旋轉

同理,以某一個節點為軸,它的右子枝逆針旋轉,作為新子樹的根,我們稱之為逆時針旋轉(anticlockwise)或者左旋轉

3. AVL插入操作

AVL樹的插入操作首先會按照普通搜尋二叉樹的插入操作進行,當插入一個數據後,我們會沿著插入資料時所經過的的節點回溯,回溯的過程中會判回溯路徑中的每個節點的左子支高度與右子支高度之差的絕對值是否超過1,如果超過1我們就進行調整,調整的目的是使得該節點滿足AVL樹的定義。調整的情況可以分為以下四旋轉操作,旋轉操作可以降低樹的高度,同時不改變搜尋二叉樹的性質(即任何一個節點左子支中的全部節點小於該節點,右子支的全部節點大於該節點)。

3.1 情況1

節點X左子支比右子支高度大2,且插入的節點位於X的左孩子節點XL的左子支上

image

3.2 情況2

節點X右子支比左子支高度大2,且插入的節點位於節點X右孩子節點XR的右子支上

image

3.3 情況3

節點X左子支比右子支高度大2,且插入的節點位於節點X左孩子節點XL的右子支上

image

3.4 情況4

節點X左子支比右子支高度大2,且插入的節點位於節點X左孩子節點XL的右子支上

image

4. AVL刪除操作

AVL樹的刪除操作和插入操作一樣,首先會按照普通搜尋二叉樹的刪除操作進行,當刪除一個數據後,和插入操作一樣,我們通常採取的策略是沿著刪除資料時所經過的的節點回溯,回溯的過程中會判斷該節點的左子支高度與右子支高度之差的絕對值是否超過1(或者說大2),如果超過1,我們就進行調整,調整的目的是使得該節點滿足AVL樹的定義。調整的情況可以分為四種,和插入過程完全一樣,這裡不在贅述。


package datastruct;
 
import java.util.Comparator;
 
public class AVLtree <E>{
    private static class Node<E>{
        int h;
        E element;
        Node<E> left;
        Node<E> right;
        //由於java中不像C語言那樣有二級指標的概念,所以新增一個父類的引用,方便程式編寫
        Node<E> parent;
         
        public Node(E element, int h, Node<E> left, Node<E> right, Node<E> parent){
            this.element = element;
            this.h = h;
            this.left = left;
            this.right = right;
            this.parent = parent;
        }
    }
     
    private Node<E> root;//指向偽根節點的引用
    private int size = 0;//節點個數
    Comparator<? super E> cmp;//節點大小的比較器
     
    //如果呼叫了不帶引數的建構函式,則使用該內部類作為比較器,
    //但此時泛型E需要繼承Comparable介面,否則執行時會丟擲異常
    private static class Cmp<T> implements Comparator<T>{
        @SuppressWarnings({ "unchecked", "rawtypes" })
        @Override
        public int compare(T e1, T e2) {
            return ((Comparable)e1).compareTo(e2);
        }
         
    }
     
    //帶比較器的建構函式
    public AVLtree(Comparator<? super E> cmp){
        if(cmp == null){
            throw new IllegalArgumentException();
        }
        this.cmp = cmp;
        //建立一個偽根節點,該節點的右子支才是真正的AVL樹的根
        //使用偽根節點節點的目的是,對插入和刪除操作遞迴的形式能夠統一
        root = new Node<E>(null, -1, null, null, null);
    }
     
    //不帶比較器的建構函式
    public AVLtree(){
        this.cmp = new Cmp<E>();
        root = new Node<E>(null, -1, null, null, null);
    }
     
    //如果樹中節點有變動,從底向上逐級呼叫該函式,可以更新節點的高度
    private int getHight(Node<E> x){
        if(x == null){
            return 0;
        }else{
            return x.h;
        }
    }
     
    //求某個節點作為根時,該子樹的最小值
    private E treeMin(Node<E> x){
        while(x.left != null){
            x = x.left;
        }
        return x.element;
    }
     
    public int size(){
        return size;
    }
     
    //先根遍歷,除錯時使用
    public void preorderTraverse(){
        if(root != null){
            preorderTraverse0(root.right);
        }
    }
     
    private void preorderTraverse0(Node<E> x){
        if(x != null){
            System.out.print(x.element+" ");
            if(x.left != null){
                System.out.print(x.left.element+" ");
            }else{
                System.out.print("null  ");
            }
             
            if(x.right != null){
                System.out.print(x.right.element+" ");
            }else{
                System.out.print("null  ");
            }
            System.out.println();
            preorderTraverse0(x.left);
            preorderTraverse0(x.right);
        }
    }
     
    //逆時針旋轉(左旋),引數表示軸節點
    private void antiClockwiseRotate(Node<E> X){
        Node<E> P = X.parent;
        Node<E> XR = X.right;
        if(P.left == X){
            P.left = XR;
        }else{
            P.right = XR;
        }
        XR.parent = P;
         
        X.right = XR.left;
        if(XR.left != null){
            XR.left.parent = X;
        }
         
        XR.left = X;
        X.parent = XR;
         
        //旋轉後要更新這兩個節點的高度
        X.h = Math.max(getHight(X.left), getHight(X.right)) + 1;
        XR.h = Math.max(getHight(XR.left), getHight(XR.right)) + 1;
    }
     
    //順時針旋轉(右旋),引數表示軸節點
    private void clockwistRotate(Node<E> X){
        Node<E> P = X.parent;
        Node<E> XL = X.left;
        if(P.left == X){
            P.left = XL;
        }else{
            P.right = XL;
        }
        XL.parent = P;
         
        X.left = XL.right;
        if(XL.right != null){
            XL.right.parent = X;
        }
         
        XL.right = X;
        X.parent = XL;
         
        //旋轉後要更新這兩個節點的高度
        X.h = Math.max(getHight(X.left), getHight(X.right)) + 1;
        XL.h = Math.max(getHight(XL.left), getHight(XL.right)) + 1;
    }
     
    //
    public void insert(E e){
        insert0(root.right, e);
    }
     
    private void insert0(Node<E> x, E e){
        if(x == null){
            root.right = new Node<E>(e, 1, null, null, root);//根節點
            size++;
            return;
        }
         
        if(cmp.compare(e, x.element) > 0){
            if(x.right != null){
                insert0(x.right, e);
                int lh = getHight(x.left);
                int rh = getHight(x.right);
                if(rh - lh == 2){
                    if(cmp.compare(e, x.right.element) > 0){
                        antiClockwiseRotate(x);
                    }else{
                        clockwistRotate(x.right);
                        antiClockwiseRotate(x);
                    }
                }
            }else{
                size++;
                x.right = new Node<E>(e, 1, null, null, x);
            }
        }else
        if(cmp.compare(e, x.element) < 0){
            if(x.left != null){
                insert0(x.left, e);
                int lh = getHight(x.left);
                int rh = getHight(x.right);
                if(lh - rh == 2){
                    if(cmp.compare(e, x.left.element) < 0){
                        clockwistRotate(x);
                    }else{
                        antiClockwiseRotate(x.left);
                        clockwistRotate(x);
                    }
                }
            }else{
                size++;
                x.left = new Node<E>(e, 1, null, null, x);
            }
        }else{
            //元素已存在,我們用新的元素更新舊,
            //compare返回值等於0,並不表示兩個物件完全相等
            x.element = e;
        }
        x.h = Math.max(getHight(x.left), getHight(x.right)) + 1;
    }
     
    public boolean delete(E e){
        return delete0(root.right, e);
    }
     
    //返回值表示是否刪除成功
    private boolean delete0(Node<E> x, E e){
        if(x == null){//沒有找到待刪除的元素
            return false;
        }
         
        if(cmp.compare(e, x.element) > 0){
            boolean reval = delete0(x.right, e);
            if(reval == false){
                return false;
            }
             
            int lh = getHight(x.left);
            int rh = getHight(x.right);
            if(lh - rh == 2){
                if(getHight(x.left.left) > getHight(x.left.right)){
                    clockwistRotate(x);
                }else{
                    antiClockwiseRotate(x.left);
                    clockwistRotate(x);
                }
            }
        }else
        if(cmp.compare(e, x.element) < 0){
            boolean reval = delete0(x.left, e);
            if(reval == false){
                return false;
            }
 
            int lh = getHight(x.left);
            int rh = getHight(x.right);
            if(rh - lh == 2){
                if(getHight(x.right.right) > getHight(x.right.left)){
                    antiClockwiseRotate(x);
                }else{
                    clockwistRotate(x.right);
                    antiClockwiseRotate(x);
                }
            }
        }else{//找到待刪除的元素
            Node<E> P = x.parent;
             
            if(x.left == null){//左子支為空,可直接刪除,在這一層一定不需要旋轉
                size--;
                if(P.left == x){
                    P.left = x.right;
                    if(P.left != null){
                        P.left.parent = P;
                    }
                }else{
                    P.right = x.right;
                    if(P.right != null){
                        P.right.parent = P;
                    }
                }
            }else
            if(x.right == null){//右子支為空,可直接刪除,在這一層一定不需要旋轉
                size--;
                if(P.left == x){
                    P.left = x.left;
                    if(P.left != null){                 
                        P.left.parent = P;
                    }
                }else{
                    P.right = x.left;
                    if(P.right != null){
                        P.right.parent = P;
                    }
                }
            }else{//找到待刪除的節點,用後繼節點代替,然後刪除後繼節點
                E nextVal = treeMin(x.right);
                x.element = nextVal;
                delete0(x.right, nextVal);
                int lh = getHight(x.left);
                int rh = getHight(x.right);
                if(lh - rh == 2){
                    if(getHight(x.left.left) > getHight(x.left.right)){
                        clockwistRotate(x);
                    }else{
                        antiClockwiseRotate(x.left);
                        clockwistRotate(x);
                    }
                }
            }
        }
        x.h = Math.max(getHight(x.left), getHight(x.right)) + 1;
        return true;
    }
     
    public static void main(String[] args){
        AVLtree<Integer> avl = new AVLtree<Integer>();
        /*可自行新增插入,刪除操作進行測試*/
        avl.insert(3);
        avl.insert(5);
        avl.insert(6);
        avl.insert(7);
        avl.insert(8);
        avl.insert(9);
        avl.preorderTraverse();
        System.out.println();
        System.out.println(avl.size());
         
        avl.delete(7);
        avl.delete(8);
        avl.preorderTraverse();
        System.out.println();
        System.out.println(avl.size());
    }
}

/**
 * Java 語言: AVL樹
 *
 * @author skywang
 * @date 2013/11/07
 */

public class AVLTreeTest {
    private static int arr[]= {3,2,1,4,5,6,7,16,15,14,13,12,11,10,8,9};

    public static void main(String[] args) {
        int i;
        AVLTree<Integer> tree = new AVLTree<Integer>();

        System.out.printf("== 依次新增: ");
        for(i=0; i<arr.length; i++) {
            System.out.printf("%d ", arr[i]);
            tree.insert(arr[i]);
        }

        System.out.printf("\n== 前序遍歷: ");
        tree.preOrder();

        System.out.printf("\n== 中序遍歷: ");
        tree.inOrder();

        System.out.printf("\n== 後序遍歷: ");
        tree.postOrder();
        System.out.printf("\n");

        System.out.printf("== 高度: %d\n", tree.height());
        System.out.printf("== 最小值: %d\n", tree.minimum());
        System.out.printf("== 最大值: %d\n", tree.maximum());
        System.out.printf("== 樹的詳細資訊: \n");
        tree.print();

        i = 8;
        System.out.printf("\n== 刪除根節點: %d", i);
        tree.remove(i);

        System.out.printf("\n== 高度: %d", tree.height());
        System.out.printf("\n== 中序遍歷: ");
        tree.inOrder();
        System.out.printf("\n== 樹的詳細資訊: \n");
        tree.print();

        // 銷燬二叉樹
        tree.destroy();
    }
}