1. 程式人生 > >資料結構:AVL樹的平衡調整——LL,LR,RL,RR

資料結構:AVL樹的平衡調整——LL,LR,RL,RR

AVL樹的全稱是平衡搜尋二叉樹,本質上也是一個二叉搜尋樹(BST),滿足BST樹的所有性質。

但是我們在使用二叉搜尋樹的時候,我們知道通常情況在BST中搜索一個節點的時間複雜度是O(lgn)。

最壞的情況為O(n),這種情況就是出現連續的左子樹/右子樹,如下圖所示:

這種情況其實就已經是鏈式儲存,無法將樹的優勢體現出來。

為了避免這種情況,我們就要保證這個樹隨時都是平衡的。當然要求不能那麼嚴格,不需要保證它完全平衡(每個節點的左右子樹高度差的絕對值都為0),AVL樹就要求的是隻要每個節點的左右子樹高度差的絕對值不超過1,就可以稱作平衡。

我們將“每個節點的左右子樹的高度差的絕對值”叫做平衡因子(BF)。

在AVL樹中,插入一個節點之後都會判斷是否打破了這個平衡。如果打破了,就需要對樹進行調整,讓它仍然滿足平衡的定義,並且不改變中序遍歷的正確性。這步操作就叫做旋轉。即,是否需要旋轉以及具體的旋轉實現都要包含在插入函式裡。

根據插入的位置不同,旋轉的型別也分為4種:LL,LR,RL,RR。

旋轉的幾種情況:
1.插入點位於x的左孩子的左子樹中。	左左LL  右旋。
2.插入點位於x的左孩子的右子樹中。	左右LR	較低的先左旋,轉換為LL問題,再右旋。
3.插入點位於x的右孩子的左子樹中。	右左RL	較低的先右旋,轉化為RR問題。再左旋。
4.插入點威武x的右孩子的右子樹中。	右右RR	左旋。

接下來我們就用幾個簡單的例子,來分析旋轉操作。

先給出AVL樹的定義。

typedef struct AVLTree
{
    int height;                //當前節點的高度。
    int data;
    struct AVLTree* lchild;
    struct AVLTree* rchild;
}Tree,*pTree
  1. LL & RR

如圖所示。

原本的狀態是左邊圖所示,中序遍歷為2,3.此時平衡因子分別為1,0.

在插入了一個節點之後(此時我們還沒有寫帶調整的插入函式,可以理解為使用BST樹的插入函式),我們可以看到這就成了我們最不想看見的情況——變成了鏈式儲存。此時,我們就需要進行調整了。

情況:導致失衡的節點(1)是節點node(3)的左子樹的左孩子,即為LL情況。

具體演算法如下:

1.對於節點node(值為3的節點),先取它的左孩子(值為2的節點)作為臨時節點temp;

2.將temp的右孩子作為node的左孩子;

3.再將node作為temp的右孩子;

4.更新height

5.node = temp。此時,temp就成為了原來node一樣的存在。

下圖是調整後的二叉樹:

可以看到,此時還是一個BST樹且中序遍歷的順序也沒有發生改變,還滿足了AVL的要求。

具體程式碼實現:

前置函式:

int GetHeight(pTree tree)            //獲取當前節點的高度。
{
    if(tree == nullptr) return 0;
    return tree->height;
}

bool IsBalanced(pTree tree)            //判斷是否平衡。
{
    int BF = GetHeight(tree->lchild) - GetHeight(tree->rchild);
    return abs(BF) < 2;
}

LL型旋轉函式:

pTree Rotate_LL(pTree tree)
{
    pTree temp = tree->lchild;
    tree->lchild = temp->rchild;
    temp->rchild = tree;

    //更新高度,先更新node再更新temp。
    tree->height = max(GetHeight(tree->lchild),GetHeight(tree->rchild))+1;
    temp->height = max(GetHeight(temp->lchild),GetHeight(temp->rchild))+1;

    return temp;
}

RR型由於和LL型是對稱的,所以只需要將LL中的所有左右互換就可以了。

pTree Rotate_RR(pTree tree)
{
    pTree temp = tree->rchild;
    tree->rchild = temp->lchild;
    temp->lchild = tree;

    //更新高度,先更新node再更新temp。
    tree->height = max(GetHeight(tree->lchild),GetHeight(tree->rchild))+1;
    temp->height = max(GetHeight(temp->lchild),GetHeight(temp->rchild))+1;

    return temp;
}
  • LR & RL 

接下來分析LR型:

即,引起失衡的是node節點的左子樹的右孩子。 即如下圖的情況:

在節點值為4的節點插入5,是4的右孩子。此時已經失衡。即為LR問題。

LR具體演算法:

1.先獲取node(值為10的節點)的左孩子節點,記為temp。

2.對temp進行RR。

3.對node進行LL。

程式碼實現:

pTree Rotate_LR(pTree tree)
{
    pTree temp = tree->lchild;
    tree->lchild = Rotate_RR(temp);
    return Rotate_LL(tree);
}

由於RL和LR是對稱的,同樣只需將所有的R換成L即可。此處不再贅述。