AVL樹的實(shí)現(xiàn)


1. 定義:帶有平衡條件的二叉搜索樹。平衡條件為其每個(gè)節(jié)點(diǎn)的左子樹和右子樹的高度最多差1。該平衡條件保證了樹的深度為O(log N).
2. 具體實(shí)現(xiàn)過程:

a. AVL樹節(jié)點(diǎn)的定義:

聲明了節(jié)點(diǎn)值,左右孩子節(jié)點(diǎn)和節(jié)點(diǎn)高度

    /**
     * 定義AVL樹的節(jié)點(diǎn)
     * @param <T>
     */
    private class AVLTreeNode<T extends Comparable<T>>{
        T key;//節(jié)點(diǎn)值
        int height;//節(jié)點(diǎn)高度
        AVLTreeNode<T> left;
        AVLTreeNode<T> right;
        public AVLTreeNode(T key, AVLTreeNode<T> left, AVLTreeNode<T> right){
            this.key = key;
            this.left = left;
            this.right = right;
            this.height = 0;
        }
    }
b. AVL樹中節(jié)點(diǎn)的查找:

先比較節(jié)點(diǎn)值與傳入的值,再遞歸查找左右子樹

    /**
     * 根據(jù)傳入的值和樹,實(shí)現(xiàn)在樹中查找是否存在某個(gè)節(jié)點(diǎn)值為key
     * @param x
     * @param key
     * @return
     */
    private AVLTreeNode<T> search(AVLTreeNode<T> x, T key){
        if (x == null){
            return null;
        }
        //進(jìn)行比較
        int cmp = key.compareTo(x.key);
        //查找左子樹
        if (cmp < 0){
            return search(x.left, key);
        } 
        //查找右子樹
        else if (cmp > 0){
            return search(x.right, key);
        }
        //查找成功,返回
        else {
            return x;
        }
    }

    public AVLTreeNode<T> search(T key){
        return search(root, key);
    }

非遞歸版查找

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

    public AVLTreeNode<T> iterativeSearch(T key){
        return iterativeSearch(root, key);
    }
3. 查找AVL樹中的最值:

最大值:不斷向右子樹查找,直到某個(gè)節(jié)點(diǎn)的右孩子節(jié)點(diǎn)為空,則返回當(dāng)前節(jié)點(diǎn)

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

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

最小值:不斷向左子樹查找,直到某個(gè)節(jié)點(diǎn)的左孩子節(jié)點(diǎn)為空,則返回當(dāng)前結(jié)點(diǎn)

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

    private AVLTreeNode<T> minimum(AVLTreeNode<T> tree){
        if (tree == null){
            return null;
        }
        while (tree.left != null){
            tree = tree.left;
        }
        return tree;
    }
4. 對(duì)AVL樹進(jìn)行插入節(jié)點(diǎn):

當(dāng)我們進(jìn)行插入操作時(shí),若不會(huì)破壞AVL樹平衡的特性,則直接插入即可,否則在插入完成之前我們要進(jìn)行一些簡(jiǎn)單的修正,我們稱其為旋轉(zhuǎn)。

出現(xiàn)不平衡的四種情況,我們假定出現(xiàn)不平衡的節(jié)點(diǎn)為a.

1. 對(duì) a 的左兒子的左子樹進(jìn)行一次插入
2. 對(duì) a 的左兒子的右子樹進(jìn)行一次插入
3. 對(duì) a 的右兒子的右子樹進(jìn)行一次插入
4. 對(duì) a 的右兒子的左子樹進(jìn)行一次插入
其中情況1和情況3,我們通過對(duì)樹的一次單旋轉(zhuǎn)即可完成,而對(duì)于情況2和情況4,我們需要通過雙旋轉(zhuǎn)完成。

單旋轉(zhuǎn):

LL的旋轉(zhuǎn),即情況1:

single_rotation
    /**
     * 傳入不平衡的節(jié)點(diǎn),進(jìn)行LL旋轉(zhuǎn),返回平衡后的節(jié)點(diǎn)
     * @param k2
     * @return
     */
    private AVLTreeNode<T> leftLeftRotation(AVLTreeNode<T> k2){
        AVLTreeNode<T> k1;
        //進(jìn)行旋轉(zhuǎn)
        k1 = k2.left;
        k2.left = k1.right;
        k1.right = k2;
        //更新節(jié)點(diǎn)的高度
        k2.height = Math.max(height(k2.left), height(k2.right)) + 1;
        k1.height = Math.max(height(k1.left), k2.height) + 1;
        return k1;
    }

RR旋轉(zhuǎn),即情況3:

RR_rotation
    /**
     * 實(shí)現(xiàn)RR旋轉(zhuǎn)
     * @param k1
     * @return
     */
    private AVLTreeNode<T> rightRightRotation(AVLTreeNode<T> k1){
        AVLTreeNode<T> k2;
        //進(jìn)行旋轉(zhuǎn)
        k2 = k1.right;
        k1.right = k2.left;
        k2.left = k1;
        //更新高度
        k1.height = Math.max(height(k1.left), height(k1.right)) + 1;
        k2.height = Math.max(height(k2.right),k1.height) + 1;
        return k2;
    }
雙旋轉(zhuǎn):

LR旋轉(zhuǎn),即情況2

LR_Rotation
    /**
     * 實(shí)現(xiàn)LR旋轉(zhuǎn)
     * @param k3
     * @return
     */
    private AVLTreeNode<T> leftRightRotation(AVLTreeNode<T> k3){
        //獲取到k1節(jié)點(diǎn)
        AVLTreeNode<T> k1 = k3.left;
        //對(duì)k1進(jìn)行RR旋轉(zhuǎn)
        k3.left = rightRightRotation(k1);
        //再對(duì)k3進(jìn)行LL旋轉(zhuǎn)
        return leftLeftRotation(k3);
    }

RL旋轉(zhuǎn),即情況4:

RL_Rotation
    /**
     * 實(shí)現(xiàn)RL旋轉(zhuǎn)
     * @param k1
     * @return
     */
    private AVLTreeNode<T> rightLeftRotation(AVLTreeNode<T> k1){
        //獲取到k3節(jié)點(diǎn)
        AVLTreeNode<T> k3 = k1.right;
        //對(duì)k3節(jié)點(diǎn)進(jìn)行LL旋轉(zhuǎn)
        k1.right = leftLeftRotation(k3);
        //對(duì)k1節(jié)點(diǎn)進(jìn)行RR旋轉(zhuǎn)
        return rightRightRotation(k1);
    }

實(shí)現(xiàn)插入節(jié)點(diǎn)操作:

    /**
     * 實(shí)現(xiàn)插入節(jié)點(diǎn)的操作
     * @param tree
     * @param key
     * @return
     */
    private AVLTreeNode<T> insert(AVLTreeNode<T> tree, T key){
        //若當(dāng)前結(jié)點(diǎn)為空,則創(chuàng)建一個(gè)新節(jié)點(diǎn),并返回
        if (tree == null){
            return new AVLTreeNode<T>(key, null, null);
        }
        //進(jìn)行比較
        int cmp = key.compareTo(tree.key);
        //進(jìn)入左子樹
        if (cmp < 0){
            //對(duì)左子樹進(jìn)行遞歸插入
            tree.left = insert(tree.left, key);
            //若插入后,存在不平衡情況
            if (height(tree.left) - height(tree.right) == 2){
                //若插入的位置為左邊,則進(jìn)行LL旋轉(zhuǎn)
                if (key.compareTo(tree.left.key) < 0){
                    tree = leftLeftRotation(tree);
                } 
                //若插入的位置在右邊,則進(jìn)行LR旋轉(zhuǎn)
                else {
                    tree = leftRightRotation(tree);
                }
            }
        } 
        //進(jìn)入右子樹
        else if (cmp > 0){
            //對(duì)右子樹進(jìn)行遞歸插入
            tree.right = insert(tree.right, key);
            //若插入后,存在不平衡的情況
            if (height(tree.right) - height(tree.left) == 2){
                //若插入的位置在右邊,則進(jìn)行RR旋轉(zhuǎn)
                if (key.compareTo(tree.right.key) > 0){
                    tree = rightRightRotation(tree);
                } 
                //若插入的位置在左邊,則進(jìn)行RL旋轉(zhuǎn)
                else {
                    tree = rightLeftRotation(tree);
                }
            }
        }
        //存在該節(jié)點(diǎn)了,不做操作
        else {
            System.out.println("添加失敗:不允許添加相同的節(jié)點(diǎn)");
        }
        //更新節(jié)點(diǎn)的高度
        tree.height = Math.max(height(tree.left), height(tree.right)) + 1;
        return tree;
    }

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

實(shí)現(xiàn)刪除節(jié)點(diǎn)操作:

    /**
     * 實(shí)現(xiàn)刪除節(jié)點(diǎn)操作,并返回刪除的節(jié)點(diǎn)
     * @param tree
     * @param delete
     * @return
     */
    private AVLTreeNode<T> remove(AVLTreeNode<T> tree, AVLTreeNode<T> delete){
        //沒有刪除的節(jié)點(diǎn),直接返回空
        if (tree == null || delete == null){
            return null;
        }
        //進(jìn)行比較
        int cmp = delete.key.compareTo(tree.key);
        //進(jìn)入左子樹
        if (cmp < 0){
            //對(duì)左邊進(jìn)行遞歸刪除
            tree.left = remove(tree.left, delete);
            //存在不平衡的情況
            if (height(tree.right) - height(tree.left) == 2){
                //因?yàn)閯h除的是左子樹節(jié)點(diǎn),則要調(diào)整的為右子樹節(jié)點(diǎn)
                AVLTreeNode<T> r =tree.right;
                //左大于右,則進(jìn)行RL旋轉(zhuǎn)
                if (height(r.left) > height(r.right)){
                    tree = rightLeftRotation(tree);
                } 
                //右高于左,則進(jìn)行RR旋轉(zhuǎn)
                else {
                    tree = rightRightRotation(tree);
                }
            }
        } 
        //進(jìn)入右子樹
        else if (cmp > 0){
            //進(jìn)行遞歸刪除
            tree.right = remove(tree.right, delete);
            //出現(xiàn)不平衡的情況
            if (height(tree.left) - height(tree.right) == 2){
                //刪除的是右子樹節(jié)點(diǎn),因此要調(diào)整的是左子樹
                AVLTreeNode<T> l = tree.left;
                //右高于左,則進(jìn)行LR旋轉(zhuǎn)
                if (height(tree.right) > height(tree.left)){
                    tree = leftRightRotation(tree);
                } 
                //左高于右,則進(jìn)行LL旋轉(zhuǎn)
                else {
                    tree = leftLeftRotation(tree);
                }
            }
        } 
        //進(jìn)行刪除當(dāng)前結(jié)點(diǎn)
        else {
            //左右孩子都不為空
            if ((tree.left != null) && (tree.right != null)){
                //左高于右,那么要調(diào)整的是左子樹
                if (height(tree.left) > height(tree.right)){
                    //獲取到左子樹的最大節(jié)點(diǎn),因?yàn)榇斯?jié)點(diǎn)不含孩子
                    AVLTreeNode<T> max = maximum(tree.left);
                    //左子樹最大節(jié)點(diǎn)替換當(dāng)前結(jié)點(diǎn)
                    tree.key = max.key;
                    //刪除左子樹的最大節(jié)點(diǎn)
                    tree.left = remove(tree.left,max);
                } else {
                    //獲取右子樹的最小節(jié)點(diǎn)
                    AVLTreeNode<T> min = minimum(tree.right);
                    //用右子樹的最小節(jié)點(diǎn)替換當(dāng)前結(jié)點(diǎn)
                    tree.key = min.key;
                    //刪除右子樹的最小節(jié)點(diǎn)
                    tree.right = remove(tree.right, min);
                }
            }
            //若只存在左孩子或右孩子,則使孩子替換當(dāng)前結(jié)點(diǎn)
            else {
                tree = (tree.left != null) ? tree.left : tree.right;
            }
        }
        return tree;
    }

    public void remove(T key){
        AVLTreeNode<T> z = search(root, key);
        if (z != null){
            root = remove(root, z);
        }
    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,763評(píng)論 6 539
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,238評(píng)論 3 428
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,823評(píng)論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,604評(píng)論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,339評(píng)論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,713評(píng)論 1 328
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,712評(píng)論 3 445
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,893評(píng)論 0 289
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,448評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,201評(píng)論 3 357
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,397評(píng)論 1 372
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,944評(píng)論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,631評(píng)論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,033評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,321評(píng)論 1 293
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,128評(píng)論 3 398
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,347評(píng)論 2 377

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

  • 基于樹實(shí)現(xiàn)的數(shù)據(jù)結(jié)構(gòu),具有兩個(gè)核心特征: 邏輯結(jié)構(gòu):數(shù)據(jù)元素之間具有層次關(guān)系; 數(shù)據(jù)運(yùn)算:操作方法具有Log級(jí)的平...
    yhthu閱讀 4,300評(píng)論 1 5
  • 樹的概述 樹是一種非常常用的數(shù)據(jù)結(jié)構(gòu),樹與前面介紹的線性表,棧,隊(duì)列等線性結(jié)構(gòu)不同,樹是一種非線性結(jié)構(gòu) 1.樹的定...
    Jack921閱讀 4,473評(píng)論 1 31
  • 1 序 2016年6月25日夜,帝都,天下著大雨,拖著行李箱和同學(xué)在校門口照了最后一張合照,搬離寢室打車去了提前租...
    RichardJieChen閱讀 5,125評(píng)論 0 12
  • 一直以來(lái),我都很少使用也避免使用到樹和圖,總覺得它們神秘而又復(fù)雜,但是樹在一些運(yùn)算和查找中也不可避免的要使用到,那...
    24K男閱讀 6,779評(píng)論 5 14
  • 記得那年他二十二歲,也正值民國(guó)二十二年。早已成年的他漸漸少了來(lái)自父母的約束,便開始計(jì)劃出游一番。時(shí)間恰好定在他...
    沈清和_閱讀 200評(píng)論 0 0