1. 程式人生 > >平衡二叉樹旋轉詳解

平衡二叉樹旋轉詳解

  • 平衡二叉樹的定義(AVL)定義
    平衡二叉樹或者是一棵空樹,或者滿足以下的性質:它的左子樹和右子樹的高度之差的絕對值不超過1,並且左子樹和右子樹也是一個平衡二叉樹。

  • 平衡因子
    左子樹高度減去右子樹的高度的值或者右子樹高度減去左子樹高度的值。顯然 -1 <=bf <= 1

  • AVL樹的引入
    平衡二叉樹在二叉排序樹上引入的,在二叉樹中,如果插入的節點接近有序,那麼二叉樹就會退化為連結串列大大降低了查詢效率,為了使二叉樹無論什麼情況下最大限度的接近滿二叉樹,從而保證它的查詢效率,因此引入平衡二叉樹。

  • AVL樹的實現
    如何保證二叉樹在任何情況下都能最大限度接近滿二叉樹,從而保證它的查詢效率呢?那就是旋轉,當平衡因子的絕對值大於1的時候,我們就需要對其進行旋轉。

  • 旋轉步驟詳解
    我們只需要理清需要旋轉的情況,掌握旋轉的要素,找到其中的規律,就能輕鬆地寫出AVL樹的程式碼了,下面我詳細的給出旋轉的情況與步驟,大家仔細觀察找出其中的規律。(此例中的平衡因子bf採用的是右子樹的高度減去左子樹的高度
    左左(LL)(右旋)
    這裡寫圖片描述
    這裡寫圖片描述
    這裡寫圖片描述

    針對於在較高左子樹的左側插入節點的這種情況大家發現了什麼規律呢?我們發現在插入之前Parent是離插入節點最近有可能失衡的節點,對於LL的三種情況我們發現插入節點導致失衡後圖示(Parent) 節點平衡因子為-2,他的左孩子(SubL)為-1,進行右旋了之後Parent 以及 SubL節點的平衡因子都變成了0。通過這些規律我們來實現右旋的程式碼
    右旋操作程式碼

void rRotate(Node *Parent)//LL
    {
        Node *subL = Parent->_pLeft;
        Node *subLR = subL->_pRight;
        Parent->_pLeft = subLR;
        if (subLR)//左單支
            subLR->_parent = Parent;
        subL->_pRight = Parent;
        Node *pParent = Parent->_parent;
        Parent
->_parent = subL; subL->_parent = pParent; if (NULL == pParent)//Parent是根節點 _pRoot = subL; else if (Parent == pParent->_pLeft) pParent->_pLeft = subL; else pParent->_pRight = subL; //修改平衡因子 subL->_bf = 0; Parent->_bf = 0; }

右右(RR)左旋
這裡寫圖片描述
這裡寫圖片描述
這裡寫圖片描述
針對於在較高右子樹的右側插入節點的這種情況大家發現了什麼規律呢?我們發現在插入之前Parent是離插入點最近有可能失衡的節點,對於RR的三種情況我們發現插入節點導致失衡後圖示(Parent) 節點平衡因子為2,他的右孩子(SubR)為1,進行左旋了之後Parent 以及 SubR節點的平衡因子都變成了0。通過規律寫出RR情況左旋的程式碼。

void lRotate(Node *Parent)//RR
    {
        Node *subR = Parent->_pRight;
        Node *subRL = subR->_pLeft;
        Parent->_pRight = subRL;
        if (subRL)
        subRL->_parent = Parent;
        subR->_pLeft = Parent;
        subR->_parent = Parent->_parent;
        Parent->_parent = subR;
        Node *pParent = subR->_parent;
        if (NULL == pParent)
            _pRoot = subR;
        else if (Parent == pParent->_pLeft)
            pParent->_pLeft = subR;
        else
            pParent->_pRight = subR;
        Parent->_bf = 0;
        subR->_bf = 0;
    }

左右(LR)
這裡寫圖片描述
這裡寫圖片描述
這裡寫圖片描述
針對於在較高左子樹的右側插入節點的這種情況大家發現了什麼規律呢?我們發現在插入之前Parent是離插入點最近有可能失衡的節點,對於LR的三種情況我們發現插入節點導致失衡後圖示(Parent) 節點平衡因子為-2,他的左孩子(SubL)為1,插入及旋轉發現旋轉之前SubLR為-1,旋轉之後Parent為1,SubLR為0,旋轉之前SubLR為1,旋轉之後SubL為-1,SubLR為0,通過規律寫出LR情況左旋的程式碼。

void lrRotate(Node *Parent)//LR
    {
        Node *subL = Parent->_pLeft;
        Node *subLR = subL->_pRight;
        int bf = subLR->_bf;
        lRotate(Parent->_pLeft);
        rRotate(Parent);
        if (1 == bf)
            subL->_bf = -1;
        else if (-1 == bf)
            Parent->_bf = 1;
           subLR->_bf = 0;
    }

右左(RL)
這裡寫圖片描述
這裡寫圖片描述
這裡寫圖片描述
針對於在較高右子樹的左側插入節點的這種情況大家發現了什麼規律呢?我們發現在插入之前Parent是離插入點最近有可能失衡的節點,對於RL的三種情況我們發現插入節點導致失衡後圖示(Parent) 節點平衡因子為2,他的右孩子(SubR)為-1,插入及旋轉發現旋轉之前SubLR為-1,旋轉之後SubR為1,SubLR為0,旋轉之前SubRL為1,旋轉之後Parent為-1,SubLR為0,通過規律寫出RL情況的程式碼。

void rlRotate(Node *Parent)
    {
        Node *subR = Parent->_pRight;
        Node *subRL = subR->_pLeft;
        int bf = subRL->_bf;
        rRotate(Parent->_pRight);
        lRotate(Parent);
        if (1 == bf)
            Parent->_bf = -1;
        else if (-1 == bf)
            subR->_bf = 1;
        subRL->_bf = 0;
    }

綜上我們不難總結出AVL樹插入的規律
這裡寫圖片描述

  • AVL樹插入程式碼
bool Insert(const K& key, const V& value)
    {
        Node *pNew = new Node(key,value);
        Node *pCur = _pRoot;
        Node *parent = NULL;
        if (NULL == _pRoot)
        {
            _pRoot = pNew;
            return true;
        }
        while (pCur)//尋找插入位置
        {
            if (key < pCur->_key)
            {
                parent = pCur;
                pCur = pCur->_pLeft;
            }
            else if (key > pCur->_key)
            {
                parent = pCur;
                pCur = pCur->_pRight;
            }
            else
                return false;
        }
        if (key < parent->_key)//插入元素
            parent->_pLeft = pNew;
        else
            parent->_pRight = pNew;
        pNew->_parent = parent;
        //修改平衡因子
        while (parent)
        {
            if (pNew == parent->_pLeft)
                parent->_bf--;
            else
                parent->_bf++;
            if (0 == parent->_bf)
                return true;
            else if (1 == parent->_bf || -1 == parent->_bf)
            {
                pNew = parent;
                parent = parent->_parent;
            }
            else//2需要進行旋轉
            {
                if (-2 == parent->_bf && -1 == pNew->_bf)//LL
                    rRotate(parent);
                else if (2 == parent->_bf && 1 == pNew->_bf)//RR
                    lRotate(parent);
                else if (-2 == parent->_bf && 1 == pNew->_bf)//LR
                    lrRotate(parent);
                else if (2 == parent->_bf && -1 == pNew->_bf)//RL
                    rlRotate(parent);
                return true;
            }
        }
        return true;
    }