1. 程式人生 > 實用技巧 >平衡二叉樹(AVL樹)

平衡二叉樹(AVL樹)

為什麼要在二叉搜尋樹的基礎上提出平衡二叉樹?

考慮這樣一種情況:

當我們的二叉搜尋樹結構如圖所示時,

這棵樹與單鏈表的查詢,刪除時間複雜度相比相差無幾,甚至高於單鏈表(二叉搜尋樹還需要判斷左子樹)

為了避免上述不平衡的儲存結構出現

以及保證較高的查詢效率,提出了平衡二叉樹

平衡二叉樹定義:任意節點的子樹的高度差都小於等於1

基於Java實現平衡二叉搜尋樹(AVL樹)

為了減少文章篇幅,本文基於上篇部落格的二叉樹程式碼完成AVL樹的構建,並主要講解平衡二叉樹的旋轉問題。

關於二叉排序樹的詳細構建,程式碼見:二叉排序樹

首先實現getHeight方法得到當前樹的高度和其子樹的高度:

//返回根結點的高度
public int height(){ return Math.max(left == null ? 0 : left.height(),right == null ? 0 : right.height())+1; }

遞迴思想解釋:以根結點左結點和右節點為初始遞迴條件,一直往下遞迴到葉子結點,往上每返回一層遞迴函式高度加一,比較左右結點路徑的深度,得到最大深度

獲得當前結點的左子結點和右子結點深度的思想類似:

 //返回左子樹的高度
    public int LeftHight(){
        if(left == null){
            
return 0; }else{ return left.height(); } }
//返回右子樹的高度
public int rightHeight() { if(right==null){ return 0; }else{ return right.height(); } }

接著考慮一般情況下的左旋和右旋情形:

左旋:該樹滿足所有結點的右側樹高度均大於左側樹高度

由於右邊比左邊高,所以很容易想到將根節點從5移動到8,對結點8周圍的三條連線線做處理,為了滿足二叉樹的定義,

將6接到結點5上。而此時如果為了得到高度相同的左右子樹而直接修改根節點,會涉及到大量的修改。

所以為了方便起見,在右樹中刪除一個結點,在左樹中新增一個結點

左旋程式碼實現:

public void leftRotate(){
        //建立新的結點儲存資訊
        Node newNode = new Node(value);
        newNode.left = left;
        newNode.right = right.left;
        value = right.value;
        right = right.right;
        left = newNode;
    }

單次右旋思路一致:所有結點的左子樹高度大於其右子樹高度即單次右旋

public void rightRotate(){
        Node newNode = new Node(value);
        newNode.right = right;
        newNode.left = left.right;
        value = left.value;
        left = left.left;
        right = newNode;
    }

接著考慮雙旋轉問題的解決:

此時通過單次左旋無法使樹恢復平衡狀態,可以單看結點11,結點11的左子樹高度為2,右子樹為0,剛好與整體樹失衡的方向相反,

那麼我們對結點11做一次右旋再對樹整體做一次左旋即可完成平衡二叉樹的構建。

if(rightHeight()-LeftHight() > 1){
            if(left != null && left.LeftHight() > left.rightHeight()){
                left.rightRotate();
                leftRotate();
            }else {
                leftRotate();
            }
        }

對以上方法進行總結,並將其新增到add方法中:因為每新增一個新資料就需要判斷樹當前是否失衡。

整體add方法如下:

public void add(Node node){
        if(node == null){
            return;
        }
        if(this.value > node.value){
            if(this.left == null){
                this.left = node;
            }else{
                this.left.add(node);
            }
        }else{
            if(this.right == null){
                this.right = node;
            }else{
                this.right.add(node);
            }
        }
        //右旋
        if(LeftHight()-rightHeight() > 1){
            if(right != null && left.rightHeight() > left.LeftHight()){
                left.leftRotate();
                rightRotate();
            }else {
                rightRotate();
            }
            return;
        }
        //左旋
        if(rightHeight()-LeftHight() > 1){
            if(left != null && left.LeftHight() > left.rightHeight()){
                left.rightRotate();
                leftRotate();
            }else {
                leftRotate();
            }
        }
    }