1. 程式人生 > >Root of AVL Tree (25 分)---AVL樹的證明式分析

Root of AVL Tree (25 分)---AVL樹的證明式分析

(第一篇)記PAT習題,AVL樹的原理的見解

04-樹5 Root of AVL Tree (25 分)

 

  

 題目意思就是給出一個N,接著插入N個值,以AVL樹的方式插入,然後求AVL樹的根節點

 

首先是樹的節點結構體,網上有人實現是節點本身儲存了一個額外資訊就是樹高,而不儲存平衡因子這個資訊。而我的實現是儲存平衡因子不儲存樹高。

typedef struct NODE {
    int n;
    int bl;
    NODE* l;
    NODE* r;
}NODE;                //我設定的是右樹高減左樹高

 

  AVL樹的平衡調整方式有4種,分別是LL旋轉,RR旋轉,LR旋轉,RL旋轉。RR與LL旋轉是對稱的,LR與RL是對稱的。

我不知道有沒有人是和我一樣的想法的,AVL樹的四種平衡調整的一個難點在於找到“失衡節點”,如何找到失衡節點我覺得是一個很有探索空間的地方。

  我的實現方式是:對於任意一個失衡節點,它只關心它自己的失衡原因,對於一個非失衡節點,它只要返回(上報給父節點)它的高度變化原因。

為什麼這樣?    

 

 

 

   觀察這張圖,紅線左側,是“RR失衡模板”---------6失衡了(總樹高4層是為了更有通用性)。這棵樹調整為紅線右側即可恢復平衡。

  其中沒有標數字的節點代表旋轉前後不影響的節點,比如紅色節點,因為在6的右側,故比6大,所以調整後,6的右子樹空缺,若紅色節點存在,則接到6的右樹上。

  
  模擬一下RR旋轉檢測與調整過程:比如我們插入的是元素12,找到位置之後,插入12。對於12本身,因為它是從無到有,所以它的平衡因子是0,上報(遞迴的返回)父節點我----》作為一棵樹,

增高了,並且增高原因是由於自己(從無到有)。

      返回到10,10發現右樹來報它增高了,增高原因是右樹本身。檢測自己平衡因子,原來是0,現在是1.於是它又上報它的父親節點,報告,我也增高了

(為什麼用又?)增高原因是我的右兒子。

      現在到8,它的平衡因子原來是0,右樹報告它增高了,增高原因是它的右兒子。8檢查自己的平衡因子,從0到1,仍未失衡,又往上報告---》我增高了,增高原因是我的右子樹(至於是右子樹自己還是右子樹的左或右,其父並不care)。

      ok,到了6節點,它發現他的平衡因子本來就是1,右樹增高,+1,變成2.於是疾呼,來人啊!我失衡了!-----》源於右子樹(故R_旋轉),而右子樹報告其右樹長高,故為RR旋轉。

 

RR旋轉:對於原根節點,簡單來說就是右樹提上去,自己左沉,原右樹若是有左樹,那便接到自己右樹。因為自己的右樹任何元素都比自己大,故肯定不虧。

     對於RR操作,很多原始碼,把上面這行翻譯一下就可以了。重點是關於重新調整平衡因子!!

    看圖  ↑,假設藍框部分樹高n,(其實就是原根的左樹),那麼,因為根RR失衡   所以,必然是此刻右樹的高度比左樹高2,所以綠框部分樹高為n+2,因為節點只能一個一個插入。麻煩節點在

一次插入之時只會有一個 ,-—》12。10的左子肯定為空,否則在插入10的左子時就已經失衡了。再者,RR失衡,8對6的返回值是“右樹增高導致我增高”。也說明8右樹必然高於左樹,若左樹右樹一樣高,說明插入之後它的高度未變,則不會給6彙報自己增高了,而8未失衡,故8一定是右樹比左樹高1。所以灰色框裡樹高一定是n+1,橙色框裡樹高一定為n。

    再看圖右側,旋轉之後。10和自己的子節點未發生變化。故平衡因子不變。6的左樹是藍框,高n,右側是橙框,高n,故6的新平衡因子是0。同理,8的左樹必然高n+1,同右樹,8的平衡因子也一定為0。

 

    

#include<iostream>
#define CAN_NOT -5
#define REPEAT -4
#define SHOULD_NOT_BE_THERE -3
enum status {
    NON,ME,BYLEFT,BYRIGHT
};//列舉表示增高原因,未增高,自己,左,右





typedef struct NODE {
    int n;
    int bl;
    NODE* l;
    NODE* r;
}NODE;                //我設定的是右樹高減左樹高


NODE* AVL=NULL;

void RR(NODE* &root);
void RL(NODE* &root);
void LL(NODE* &root);
void LR(NODE* &root);


status Insert(NODE* &root,int a) {
    status ret;
    if (root == NULL) {
        root = new NODE;
        root->bl = 0;
        root->l = NULL;
        root->r = NULL;
        root->n = a;
        return ME;
    }//是我自己導致我的樹增高,返回ME
    else if (a > root->n) {    //往右子樹插入
        ret = Insert(root->r, a);

        if (ret == NON)
            return NON;
        else {    //右樹增高,待處理
            
            ++root->bl;
            if (root->bl <= 0)
                return NON;
            else if (root->bl < 2) {
                return BYRIGHT;
            }
            else {            //自己失衡,原因RR或RL,
                if (ret == BYRIGHT) {
                    RR(root);
                    return NON;
                }
                else if(ret==BYLEFT){
                    RL(root);                //RL旋轉
                    return NON;
                }
                else {
                    exit(CAN_NOT);//ME導致失衡,不可能
                }
            }
        }
    }
    else if (a == root->n) {
        exit(REPEAT);        //不允許相等
    }
    else {
        ret = Insert(root->l, a);
        if (ret == NON) {
            return NON;
        }
        else{
            --root->bl;
            if (root->bl >= 0) {
                return NON;        //依然OK
            }
            else if(root->bl>-2) {
                return BYLEFT;
            }
            else {
                if (ret == BYLEFT) {    //LL旋轉
                    LL(root);
                    return NON;
                }
                else if(ret==BYRIGHT) {
                    LR(root);
                    return NON;
                }
                else {
                    exit(CAN_NOT);//ME導致失衡,不可能
                }
            }            
        }
    }
    exit(SHOULD_NOT_BE_THERE);//不應該走到這個位置 好長的insert函式
}






int main() {
    int n;
    int tmp;

    NODE* AVL=NULL;

    std::cin >> n;
    for (int i = 0; i < n; ++i) {
        std::cin >> tmp;
        Insert(AVL, tmp);
    }

    std::cout << AVL->n;


    return 0;
}




void RR(NODE* &root) {        //RR旋轉
    
    NODE* tmp = root;        //root變更為右樹
    root = tmp->r;
    tmp->r = root->l;
    root->l = tmp;

    //調整平衡因子
    tmp->bl -= 2;        //推算可得
    root->bl = 0;        //大致是,因2破壞,設左樹高n,右樹高n+2,RR破壞,右右樹高n-1,右樹未破壞且右右高,右左高n+1。。。


    return;
}


void LL(NODE* &root) {//紙筆推算一下,失衡的一些確定推斷
    NODE* tmp = root;
    root = tmp->l;
    tmp->l = root->r;
    root->r = tmp;

    tmp->bl += 2;        
    root->bl = 0;    


    return;
}

void RL(NODE* &root) {

    NODE* tmp = root;
    root = tmp->r->l;

    tmp->r->l = root->r;
    root->r = tmp->r;
    tmp->r = root->l;
    root->l = tmp;        //RL旋轉複雜一些,不過都可以通過設樹高論證原理
                            
    if (root->bl == -1) {        //重新設定平衡因子,比較繁瑣,推論可知
        root->r->bl = 1;
        root->bl = 0;
        root->l->bl = 0;
    }
    else if (root->bl == 1) {
        root->r->bl = 0;
        root->bl = 0;
        root->l->bl = -1;
    }
    else {
        root->l->bl = 0;
        root->r->bl = 0;
    }


    return;
}


void LR(NODE* &root) {

    NODE* tmp = root;
    root = tmp->l->r;

    tmp->l->r = root->l;
    root->l = tmp->l;
    tmp->l = root->r;
    root->r = tmp;        

    if (root->bl == -1) {        
        root->r->bl = 1;
        root->bl = 0;
        root->l->bl = 0;
    }
    else if (root->bl == 1) {
        root->r->bl = 0;
        root->bl = 0;
        root->l->bl = -1;
    }
    else {//LR旋轉導致失衡,且root ->bl為0,說明LR是新產生的節點
        root->l->bl = 0;
        root->r->bl = 0;
    }


    return;
}

    LL旋轉的分析基本等同RR旋轉。程式碼裡有些無用的本來用於除錯可能的錯誤原因,結果意外的好,就改了一個bug就過了。

     後面是對LR的分析。下次補上。第一次寫,試試。