數(shù)據(jù)結(jié)構(gòu)--AVL樹(平衡二叉樹)

AVL樹

  • 最早的自平衡的搜索樹結(jié)構(gòu)
  • 對(duì)于任意一個(gè)節(jié)點(diǎn)晦鞋,左子樹和右子樹的高度差不能超過一间校。


  • 滿二叉樹(除了葉子節(jié)點(diǎn)之外苗沧,其他節(jié)點(diǎn)都有左右倆個(gè)子樹)是平衡二叉樹艺配。
  • 完全二叉樹(可能有一個(gè)非葉子節(jié)點(diǎn)的右子樹是空,空缺的節(jié)點(diǎn)部分在整棵樹的右下部分伙菜,整顆樹的葉子節(jié)點(diǎn)最大的深度值和最小的深度值相差不超過一轩缤,所有的葉子節(jié)點(diǎn)要么在樹的最后一層,要么在樹的倒數(shù)第二層)是平衡二叉樹贩绕。
  • 線段樹(空出來的部分不一定在整棵樹的右下角部分火的,整顆樹的葉子節(jié)點(diǎn)最大的深度值和最小的深度值相差不超過一)是平衡二叉樹。

平衡因子

二叉樹上節(jié)點(diǎn)的左子樹深度減去右子樹深度的值稱為平衡因子淑倾,那么平衡二叉樹上所有節(jié)點(diǎn)的平衡因子只可能是-1馏鹤,0,1娇哆。只要二叉樹上的有一個(gè)節(jié)點(diǎn)的平衡因子的絕對(duì)值大于1湃累,則該二叉樹就是不平衡的。

代碼示例

創(chuàng)建平衡二叉樹

public class AVLTree <K extends Comparable<K>, V> {
    private class Node{
        public K key;
        public V value;
        public Node left, right;
        public int height; //記錄當(dāng)前節(jié)點(diǎn)所處的高度值
        public Node(K key, V value){
            this.key = key;
            this.value = value;
            left = null;
            right = null;
            height = 1;
        }
    }

    private Node root;
    private int size;

    public AVLTree(){
        root = null;
        size = 0;
    }

    public int getSize() {
        return size;
    }

    public boolean isEmpty() {
        return size == 0;
    }

    private Node getNode(Node node, K key){
        if (node == null)
            return  null;
        if (key.compareTo(node.key) == 0)
            return node;
        else if (key.compareTo(node.key) < 0)
            return getNode(node.left, key);
        else
            return getNode(node.right, key);
    }

    public boolean contains(K key) {
        return getNode(root, key) != null;
    }


    public V get(K key) {
        Node node = getNode(root, key);
        return node == null? null : node.value;
    }


    public void set(K key, V newValue) {
        Node node = getNode(root, key);
        if (node == null)
            throw new IllegalArgumentException(key + "doesn`t exist");
        node.value = newValue;
    }
}

獲取高度

    //獲得節(jié)點(diǎn)node的高度
    private int getHeight(Node node) {
        if (node == null)
            return 0;
        return node.height;
    }

獲取平衡因子

    //獲取節(jié)點(diǎn)node的平衡因子
    private int getBalanceFactor(Node node) {
        if (node == null)
            return 0;
        return getHeight(node.left) - getHeight(node.right);
    }

判斷是否是二叉樹

    //判斷該二叉樹是否是一顆二分搜索樹
    public boolean isBST() {
        ArrayList<K> keys = new ArrayList<>();
        inOrder(root, keys);
        for (int i = 0; i < keys.size(); I++)
            if (keys.get(i-1).compareTo(keys.get(i)) > 0)
                return false;
        return true;
    }

    //中序遍歷
    private void inOrder(Node node, ArrayList<K> keys){
        if (node == null)
            return;
        inOrder(node.left, keys);
        keys.add(node.key);
        inOrder(node.right, keys);
    }

判斷是否是平衡二叉樹

    //判斷該二叉樹是否是一顆平衡二叉樹
    public boolean isBalanced() {
        return isBalanced(root);
    }

    //判斷以node為根的二叉樹是否是一顆平衡二叉樹
    private boolean isBalanced(Node node) {
        if (node == null)
            return true;
        int balanceFactor = getBalanceFactor(node);
        if (Math.abs(balanceFactor) > 1)
            return false;
        return isBalanced(node.left) && isBalanced(node.right);
    }
如何維護(hù)平衡迂尝,當(dāng)添加新元素時(shí)可能會(huì)破壞平衡
  • 右旋轉(zhuǎn) RR
時(shí)機(jī)
右旋轉(zhuǎn)
右旋轉(zhuǎn)
右旋轉(zhuǎn)

右旋轉(zhuǎn)代碼

    //右旋轉(zhuǎn)
    // 對(duì)節(jié)點(diǎn)y進(jìn)行向右旋轉(zhuǎn)操作脱茉,返回旋轉(zhuǎn)后新的根節(jié)點(diǎn)x
    //            y                                   x
    //          /  \                                /   \
    //         x    T4       向右旋轉(zhuǎn)(y)           z       y
    //        /  \        ------------->        /  \    /  \
    //       z    T3                           T1  T2  T3   T4
    //     /  \
    //   T1    T2

    private Node rightRotate(Node y) {
        Node x = y.left;
        Node T3 = x.right;

        //向右旋轉(zhuǎn)過程
        x.right = y;
        y.left = T3;

        //更新節(jié)點(diǎn)height值 先更新y 后更新x
        y.height = 1 + Math.max(getHeight(y.left), getHeight(y.right));
        x.height = 1 + Math.max(getHeight(x.left), getHeight(x.right));

        return x;
    }
  • 左旋轉(zhuǎn) LL
左旋轉(zhuǎn)
左旋轉(zhuǎn)

左旋轉(zhuǎn)代碼

    //左旋轉(zhuǎn)
    // 對(duì)節(jié)點(diǎn)y進(jìn)行向左旋轉(zhuǎn)操作,返回旋轉(zhuǎn)后新的根節(jié)點(diǎn)x
    //        y                                      x
    //      /  \                                   /   \
    //    T1    x          向左旋轉(zhuǎn)(y)             y      z
    //         /  \      ------------->        /  \    /  \
    //       T2    z                          T1  T2  T3   T4
    //            /  \
    //          T3    T4
    private Node leftRotate(Node y) {
        Node x = y.right;
        Node T2 = x.left;

        //向左旋轉(zhuǎn)過程
        x.left = y;
        y.right = T2;

        y.height = 1 + Math.max(getHeight(y.left), getHeight(y.right));
        x.height = 1 + Math.max(getHeight(x.left), getHeight(x.right));

        return x;
    }
  • LR
LR
先左旋
再右旋

LR代碼示例

        //LR
        if (banlanceFactor > 1 && getBalanceFactor(node.left) < 0){
            node.left = leftRotate(node.left);
            return rightRotate(node);
        }
  • RL
RL
先右旋
在左旋

RL代碼示例

        //RL
        if (banlanceFactor < -1 && getBalanceFactor(node.right) > 0){
            node.right = rightRotate(node.right);
            return leftRotate(node);
        }

添加一個(gè)元素

//向二分搜索樹種添加新元素(key, value)
    public void add(K key, V value) {
        root = add(root, key, value);
    }

    //向以node為根的二分搜索樹中插入元素(key, value),遞歸算法
    //返回插入新節(jié)點(diǎn)后二分搜索樹的根
    private Node add(Node node, K key, V value) {

        if (node == null) {
            size ++;
            return new Node(key, value);
        }

        //如果相等 則不作處理
        if (key.compareTo(node.key) < 0)
            node.left = add(node.left, key, value);
        else if (key.compareTo(node.key) > 0)
            node.right = add(node.right, key, value);
        else // ==
            node.value = value;

        //更新height
        node.height = 1 + Math.max(getHeight(node.left), getHeight(node.right));

        //計(jì)算平衡因子
        int banlanceFactor = getBalanceFactor(node);
        if (Math.abs(banlanceFactor) > 1)
            System.out.println("unbalanced: " + banlanceFactor);

        //平衡維護(hù)
        // LL
        if (banlanceFactor > 1 && getBalanceFactor(node.left) >= 0)
            return rightRotate(node);

        //RR
        if (banlanceFactor < -1 && getBalanceFactor(node.right) <= 0)
            return leftRotate(node);

        //LR
        if (banlanceFactor > 1 && getBalanceFactor(node.left) < 0){
            node.left = leftRotate(node.left);
            return rightRotate(node);
        }

        //RL
        if (banlanceFactor < -1 && getBalanceFactor(node.right) > 0){
            node.right = rightRotate(node.right);
            return leftRotate(node);
        }

        return node;
    }

刪除一個(gè)元素

    public V remove(K key) {
        Node node = getNode(root, key);
        if (node != null){
            root = remove(root, key);
            return node.value;
        }
        return null;
    }

    //刪除以node為根的二分搜索樹中值鍵為Key的節(jié)點(diǎn) 遞歸算法
    //返回刪除節(jié)點(diǎn)后新的二分搜索樹的根
    private Node remove(Node node, K key){

        if (node == null)
            return null;

        Node retNode;
        if (key.compareTo(node.key) < 0 ){
            node.left = remove(node.left, key);
            retNode =  node;
        } else if (key.compareTo(node.key) > 0 ){
            node.right = remove(node.right, key);
            retNode = node;
        } else {
            if (node.left == null) {
                Node right = node.right;
                node.right = null;
                size --;
                retNode = right;
            } else if (node.right == null) {
                Node left = node.left;
                node.left = null;
                size --;
                retNode = left;
            } else {

                //待刪除節(jié)點(diǎn)左右子樹均不為空的情況
                //找到比待刪除節(jié)點(diǎn)大的最小元素垄开,即待刪除節(jié)點(diǎn)右子樹的最小節(jié)點(diǎn)
                //用這個(gè)節(jié)點(diǎn)頂替待刪除節(jié)點(diǎn)的位置
                Node successor = minimum(node.right);
                //removeMin 沒有維護(hù)平衡 所以會(huì)影響平衡因子
                //successor.right = removeMin(node.right);

                //調(diào)用自己也會(huì)刪除 且維護(hù)平衡
                successor.right = remove(node.right, successor.key);

                successor.left = node.left;
                node.left = node.right = null;

                retNode = successor;
            }
        }

        if (retNode == null)
            return null;

        //更新height
        retNode.height = 1 + Math.max(getHeight(retNode.left), getHeight(retNode.right));

        //計(jì)算平衡因子
        int banlanceFactor = getBalanceFactor(retNode);
        if (Math.abs(banlanceFactor) > 1)
            System.out.println("unbalanced: " + banlanceFactor);

        //平衡維護(hù)
        // LL
        if (banlanceFactor > 1 && getBalanceFactor(retNode.left) >= 0)
            return rightRotate(retNode);

        //RR
        if (banlanceFactor < -1 && getBalanceFactor(retNode.right) <= 0)
            return leftRotate(retNode);

        //LR
        if (banlanceFactor > 1 && getBalanceFactor(retNode.left) < 0){
            retNode.left = leftRotate(retNode.left);
            return rightRotate(retNode);
        }

        //RL
        if (banlanceFactor < -1 && getBalanceFactor(retNode.right) > 0){
            retNode.right = rightRotate(retNode.right);
            return leftRotate(retNode);
        }

        return retNode;
    }

時(shí)間復(fù)雜度:O(logn)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末琴许,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子溉躲,更是在濱河造成了極大的恐慌榜田,老刑警劉巖益兄,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異箭券,居然都是意外死亡净捅,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門辩块,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蛔六,“玉大人,你說我怎么就攤上這事废亭」拢” “怎么了?”我有些...
    開封第一講書人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵豆村,是天一觀的道長(zhǎng)液兽。 經(jīng)常有香客問我,道長(zhǎng)掌动,這世上最難降的妖魔是什么四啰? 我笑而不...
    開封第一講書人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮粗恢,結(jié)果婚禮上柑晒,老公的妹妹穿的比我還像新娘。我一直安慰自己适滓,他們只是感情好敦迄,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開白布恋追。 她就那樣靜靜地躺著凭迹,像睡著了一般。 火紅的嫁衣襯著肌膚如雪苦囱。 梳的紋絲不亂的頭發(fā)上嗅绸,一...
    開封第一講書人閱讀 49,166評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音撕彤,去河邊找鬼鱼鸠。 笑死,一個(gè)胖子當(dāng)著我的面吹牛羹铅,可吹牛的內(nèi)容都是我干的蚀狰。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼职员,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼麻蹋!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起焊切,我...
    開封第一講書人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬榮一對(duì)情侶失蹤扮授,失蹤者是張志新(化名)和其女友劉穎芳室,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體刹勃,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡堪侯,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了荔仁。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片伍宦。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖乏梁,靈堂內(nèi)的尸體忽然破棺而出雹拄,到底是詐尸還是另有隱情,我是刑警寧澤掌呜,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布滓玖,位于F島的核電站,受9級(jí)特大地震影響质蕉,放射性物質(zhì)發(fā)生泄漏势篡。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一模暗、第九天 我趴在偏房一處隱蔽的房頂上張望禁悠。 院中可真熱鬧,春花似錦兑宇、人聲如沸碍侦。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽瓷产。三九已至,卻和暖如春枚驻,著一層夾襖步出監(jiān)牢的瞬間濒旦,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來泰國(guó)打工再登, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留尔邓,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓锉矢,卻偏偏與公主長(zhǎng)得像梯嗽,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子沽损,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344

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