AVL樹(shù)

參考:https://www.cnblogs.com/skywang12345/p/3577479.html

AVL樹(shù)本質(zhì)上還是一棵二叉搜索樹(shù),它的特點(diǎn)是:

1.本身首先是一棵二叉搜索樹(shù)猩谊。
2.帶有平衡條件:每個(gè)結(jié)點(diǎn)的左右子樹(shù)的高度之差的絕對(duì)值(平衡因子)最多為1千劈。
也就是說(shuō),AVL樹(shù)牌捷,本質(zhì)上是帶了平衡功能的二叉查找樹(shù)(二叉排序樹(shù),二叉搜索樹(shù))涡驮。

以下為四種不平衡的臨界條件暗甥,分別為L(zhǎng)L(leftleft),LR(leftright),RL,RR。


當(dāng)每次到達(dá)這四種不平衡的臨界條件時(shí)捉捅,都進(jìn)行旋轉(zhuǎn)樹(shù)操作撤防,那么樹(shù)就能維持平衡:

private AVLTreeNode leftLeftRotation(AVLTreeNode node) {
        checkNull(node);

        AVLTreeNode  leftChild = node.left;
        node.left =  leftChild.right;
         leftChild.right = node;
        //reset the height
        node.height = max(height(node.left), height(node.right)) + 1;
         leftChild.height = max(height( leftChild.left), height( leftChild.right)) + 1;
        return  leftChild;
    }

    private AVLTreeNode rightRightRotation(AVLTreeNode node) {
        checkNull(node);

        AVLTreeNode  rightChild = node.right;
        node.right =  rightChild.left;
         rightChild.left = node;

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

    private AVLTreeNode leftRightRotation(AVLTreeNode node) {
        node.left = rightRightRotation(node.left);//先對(duì)node.left旋轉(zhuǎn)
        return leftLeftRotation(node);//后對(duì)自己旋轉(zhuǎn)
    }

    private AVLTreeNode rightLeftRotation(AVLTreeNode node) {
        node.right = leftLeftRotation(node.right);
        return rightRightRotation(node);
    }

結(jié)合插入和刪除,AVL樹(shù)的實(shí)現(xiàn):

package DataStructureAndAlgor;
/**
 * AVL樹(shù)
 * 回顧的時(shí)候棒口,最好把LL,RR,LR,RL旋轉(zhuǎn)的四張圖畫(huà)出來(lái)寄月,好理解。
 *
 * @param <T>
 */
public class AVLTree<T extends Comparable<T>> {

    class AVLTreeNode {
        T key;
        int height;
        AVLTreeNode left;
        AVLTreeNode right;

        public AVLTreeNode(T key, AVLTreeNode left, AVLTreeNode right) {
            this.key = key;
            this.left = left;
            this.right = right;
            this.height = 0;//初始化的結(jié)點(diǎn)的height都是0无牵,因?yàn)槎际亲鳛槿~子結(jié)點(diǎn)添加到樹(shù)中的漾肮。
        }
    }

    private AVLTreeNode mRoot;


    private int height(AVLTreeNode tree) {
        if (tree != null) {
            return tree.height;
        }
        return 0;
    }

    public int height() {
        return height(mRoot);
    }

    public int height(T key) {
        AVLTreeNode node = search(mRoot, key);
        return height(node);
    }

    private int max(int a, int b) {
        return a > b ? a : b;
    }

    private AVLTreeNode leftLeftRotation(AVLTreeNode node) {
        checkNull(node);

        AVLTreeNode  leftChild = node.left;
        node.left =  leftChild.right;
         leftChild.right = node;
        //reset the height
        node.height = max(height(node.left), height(node.right)) + 1;
         leftChild.height = max(height( leftChild.left), height( leftChild.right)) + 1;
        return  leftChild;
    }

    private AVLTreeNode rightRightRotation(AVLTreeNode node) {
        checkNull(node);

        AVLTreeNode  rightChild = node.right;
        node.right =  rightChild.left;
         rightChild.left = node;

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

    private AVLTreeNode leftRightRotation(AVLTreeNode node) {
        node.left = rightRightRotation(node.left);//先對(duì)node.left旋轉(zhuǎn)
        return leftLeftRotation(node);//后對(duì)自己旋轉(zhuǎn)
    }

    private AVLTreeNode rightLeftRotation(AVLTreeNode node) {
        node.right = leftLeftRotation(node.right);
        return rightRightRotation(node);
    }

    private void checkNull(AVLTreeNode node) {
        if (node == null) {
            throw new NullPointerException();
        }
    }

    /**
     * 將結(jié)點(diǎn)插入到AVL樹(shù)種,并返回根節(jié)點(diǎn)茎毁。
     *
     * @param tree AVL樹(shù)的根節(jié)點(diǎn)
     * @param key  待插入的結(jié)點(diǎn)的值克懊。
     * @return 根節(jié)點(diǎn)
     */
    private AVLTreeNode insert(AVLTreeNode tree, T key) {
        if (tree == null) {//遞歸終點(diǎn)
            tree = new AVLTreeNode(key, null, null);
        } else {
            int cmp = key.compareTo(tree.key);//compare result

            if (cmp < 0) {
                tree.left = insert(tree.left, key);//遞歸插入
                //插入結(jié)點(diǎn)后,若AVLTree失去平衡七蜘,旋轉(zhuǎn)樹(shù)谭溉。
                if (height(tree.left) - height(tree.right) == 2) {//說(shuō)明是tree結(jié)點(diǎn)出問(wèn)題(必須是left-right,否則得負(fù)值)
                    if (key.compareTo(tree.left.key) < 0) {//這里如果寫(xiě)成tree.right.key會(huì)報(bào)空指針異常
                        tree = leftLeftRotation(tree);
                    } else {
                        tree = leftRightRotation(tree);
                    }
                }
            } else if (cmp > 0) {
                tree.right = insert(tree.right, key);//遞歸插入
                if (height(tree.right) - height(tree.left) == 2) {//說(shuō)明是tree結(jié)點(diǎn)出問(wèn)題(必須是left-right橡卤,否則得負(fù)值)
                    if (key.compareTo(tree.right.key) < 0) {//這里如果寫(xiě)成tree.left.key會(huì)報(bào)空指針異常
                        tree = rightLeftRotation(tree);
                    } else {
                        tree = rightRightRotation(tree);
                    }
                }
            } else {  //cmp==0;
                System.out.println("添加失敯缒睢!不允許添加相同的結(jié)點(diǎn)碧库!");
            }

        }

        tree.height = max(height(tree.left), height(tree.right)) + 1;

        return tree;
    }

    public void insert(T key) {
        mRoot = insert(mRoot, key);
    }

    /**
     * 刪除樹(shù)中的結(jié)點(diǎn)z, 返回根節(jié)點(diǎn)
     *
     * @param tree 從tree結(jié)點(diǎn)開(kāi)始找尋要?jiǎng)h除的結(jié)點(diǎn)z
     * @param z    待刪除的根節(jié)點(diǎn)
     * @return 根節(jié)點(diǎn)
     */
    private AVLTreeNode remove(AVLTreeNode tree, AVLTreeNode z) {
        if (tree == null || z == null) {
            return null;
        }

        int cmp = z.key.compareTo(tree.key);
        if (cmp < 0) {//待刪除的結(jié)點(diǎn)在tree的左子樹(shù)
            tree.left = remove(tree.left, z);
            if (height(tree.right) - height(tree.left) == 2) {//如果刪除后tree失去平衡, 進(jìn)行調(diào)節(jié)
                AVLTreeNode r = tree.right;
                if (height(r.left) > height(r.right)) {
                    tree = rightLeftRotation(tree);
                } else {
                    tree = rightRightRotation(tree);
                }
            }
        } else if (cmp > 0) {//待刪除的結(jié)點(diǎn)在tree的右子樹(shù)
            tree.right = remove(tree.right, z);
            if (height(tree.left) - height(tree.right) == 2) {//失去平衡
                AVLTreeNode l = tree.left;
                if (height(l.left) < height(l.right)) {
                    tree = leftRightRotation(tree);
                } else {
                    tree = leftLeftRotation(tree);
                }
            }
        } else {//cmp==0,tree是要?jiǎng)h除的結(jié)點(diǎn)
            if (tree.left != null && tree.right != null) {//tree的左右孩子非空
                if (height(tree.left) > height(tree.right)) {
                    /*
                    如果tree的左子樹(shù)比右子樹(shù)高柜与,則
                    (1)找出tree的左子樹(shù)中的最大結(jié)點(diǎn)
                    (2)將該最大結(jié)點(diǎn)的值賦值給tree
                     (3) 刪除該最大結(jié)點(diǎn) 。
                     采用該方法的好處是:刪除結(jié)點(diǎn)之后谈为,AVL樹(shù)仍然是平衡的旅挤。
                     */
                    AVLTreeNode max = maximum(tree.left);
                    tree.key = max.key;
                    tree.left = remove(tree.left, max);
                } else {
                    /*
                    如果tree的右子樹(shù)比左子樹(shù)高或相等,則
                    (1) 找出tree的右子樹(shù)中的最小結(jié)點(diǎn)
                    (2) 將該最小結(jié)點(diǎn)的值賦值給tree
                     (3) 刪除該最大結(jié)點(diǎn)
                    采用該方法的好處是:刪除結(jié)點(diǎn)之后伞鲫,AVL樹(shù)仍然是平衡的粘茄。
                     */
                    AVLTreeNode min = minimum(tree.right);
                    tree.key = min.key;
                    tree.right = remove(tree.right, min);
                }
            } else {
                tree = (tree.left != null) ? tree.left : tree.right;
            }
        }

        if (tree != null) {//刪除葉子結(jié)點(diǎn)的時(shí)候,這個(gè)tree是會(huì)返回null的。
            tree.height = max(height(tree.left), height(tree.right)) + 1;
        }


        return tree;
    }

    public void remove(T key) {
        AVLTreeNode z = search(mRoot, key);
        if (z != null) {
            mRoot = remove(mRoot, z);
        }
    }

    private void preOrderPrint(AVLTreeNode root, int depth) {
        if (root != null) {
            System.out.println();//換行
            for (int i = 0; i < depth; i++) {//for循環(huán)來(lái)打印value前的空格
                System.out.print("--");//結(jié)點(diǎn)深度越大柒瓣,空格越多
            }
            System.out.print(root.key);
            depth++;
            preOrderPrint(root.left, depth);
            preOrderPrint(root.right, depth);
        }
    }

    public void print() {
        preOrderPrint(mRoot, 0);
    }


    private AVLTreeNode search(AVLTreeNode node, T key) {
        if (node == null) {
            return null;
        }
        int cmp = key.compareTo(node.key);
        if (cmp < 0) {
            return search(node.left, key);
        } else if (cmp > 0) {
            return search(node.right, key);
        } else {
            return node;
        }

    }

    private AVLTreeNode maximum(AVLTreeNode tree) {
        if (tree == null) {
            return null;
        }
        while (tree.right != null) {
            tree = tree.right;
        }
        return tree;
    }

    private AVLTreeNode minimum(AVLTreeNode tree) {
        if (tree == null) {
            return null;
        }
        while (tree.left != null) {
            tree = tree.left;
        }
        return tree;
    }

    public T maximum() {
        AVLTreeNode p = maximum(mRoot);
        if (p != null) {
            return p.key;
        }
        return null;
    }

    public T minimum() {
        AVLTreeNode p = minimum(mRoot);
        if (p != null) {
            return p.key;
        }
        return null;
    }
}

測(cè)試:

public class Main {
    public static void main(String[] args) {
        AVLTree<Integer> tree = new AVLTree<>();
        int arr[] = {33, 22, 4, 7, 0, 60, 13, 32, 21, 99, 6, 15, 27, 2};

        for (int i = 0; i < arr.length; i++) {
            tree.insert(arr[i]);
        }

        tree.print();
        System.out.println();
        System.out.println("樹(shù)的根結(jié)點(diǎn)高度:" + tree.height());

        System.out.println("刪除結(jié)點(diǎn)33,32,27,60,99");

        tree.remove(33);
        tree.remove(32);
        tree.remove(27);
        tree.remove(60);
        tree.remove(99);

        tree.print();
        System.out.println();
        System.out.println("樹(shù)的根結(jié)點(diǎn)高度:" + tree.height());
    }
}

打尤宕睢:

22
--7
----4
------0
--------2
------6
----15
------13
------21
--33
----32
------27
----60
------99
樹(shù)的根結(jié)點(diǎn)高度:5
99
0
刪除結(jié)點(diǎn)33,32,27,60,99

7
--4
----0
------2
----6
--15
----13
----22
------21
樹(shù)的根結(jié)點(diǎn)高度:4
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市芙贫,隨后出現(xiàn)的幾起案子搂鲫,更是在濱河造成了極大的恐慌,老刑警劉巖磺平,帶你破解...
    沈念sama閱讀 216,692評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件魂仍,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡拣挪,警方通過(guò)查閱死者的電腦和手機(jī)擦酌,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,482評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)菠劝,“玉大人赊舶,你說(shuō)我怎么就攤上這事「险铮” “怎么了笼平?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,995評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)舔痪。 經(jīng)常有香客問(wèn)我寓调,道長(zhǎng),這世上最難降的妖魔是什么辙喂? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,223評(píng)論 1 292
  • 正文 為了忘掉前任捶牢,我火速辦了婚禮,結(jié)果婚禮上巍耗,老公的妹妹穿的比我還像新娘秋麸。我一直安慰自己,他們只是感情好炬太,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,245評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布灸蟆。 她就那樣靜靜地躺著,像睡著了一般亲族。 火紅的嫁衣襯著肌膚如雪炒考。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,208評(píng)論 1 299
  • 那天霎迫,我揣著相機(jī)與錄音斋枢,去河邊找鬼。 笑死知给,一個(gè)胖子當(dāng)著我的面吹牛瓤帚,可吹牛的內(nèi)容都是我干的描姚。 我是一名探鬼主播,決...
    沈念sama閱讀 40,091評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼戈次,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼轩勘!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起怯邪,我...
    開(kāi)封第一講書(shū)人閱讀 38,929評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤绊寻,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后悬秉,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體澄步,經(jīng)...
    沈念sama閱讀 45,346評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,570評(píng)論 2 333
  • 正文 我和宋清朗相戀三年和泌,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了驮俗。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,739評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡允跑,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出搪柑,到底是詐尸還是另有隱情聋丝,我是刑警寧澤,帶...
    沈念sama閱讀 35,437評(píng)論 5 344
  • 正文 年R本政府宣布工碾,位于F島的核電站弱睦,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏渊额。R本人自食惡果不足惜况木,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,037評(píng)論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望旬迹。 院中可真熱鬧火惊,春花似錦、人聲如沸奔垦。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,677評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)椿猎。三九已至惶岭,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間犯眠,已是汗流浹背按灶。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,833評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留筐咧,地道東北人鸯旁。 一個(gè)月前我還...
    沈念sama閱讀 47,760評(píng)論 2 369
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親羡亩。 傳聞我的和親對(duì)象是個(gè)殘疾皇子摩疑,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,647評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • 18年6月份雷袋,我就要告別大學(xué)這座象牙塔,獨(dú)自去面對(duì)社會(huì)了辞居】回首寒窗苦讀的17年里,似乎在每個(gè)階段里瓦灶,我都對(duì)自...
    寒月而君臨閱讀 248評(píng)論 0 0
  • 音名 唱名 C Do C#(Db) Di(Ra) D Re D#(Eb) Ri(...
    12semitones閱讀 1,811評(píng)論 0 0