七、二叉樹(九)、平衡二叉樹

數據結構目錄

一、定義(AVL樹)

平衡二叉樹定義(AVL):
要么它是一顆空樹,或者具有以下性質的二叉排序樹:它的左子樹和右子樹都是平衡二叉樹,且左子樹和右子樹的深度之差的絕對值不超過1.

左邊不是二叉排序樹
左邊45結點左右深度之差為2,大于1

二、平衡二叉樹相關概念

平衡因子BF(Balance Factor):
將二叉樹上結點的左子樹高度減去右子樹高度的值稱為該結點的平衡因子BF(Balance Factor)。

也就是說,對于平衡二叉樹,BF的取值范圍為[-1,1],如果發現某個結點的BF值不在此范圍,則需要對樹進行調整。

最小不平衡樹
距離插入結點最近的,且平衡因子的絕對值大于1的結點的根的子樹。

最小不平衡樹

如圖所示,左邊二叉樹的結點45的BF=1,而在插入43結點后,45結點的BF=2。結點45是距離插入結點43最近的BF不在[-1,1]范圍內的結點,因此以結點45為根節點的子樹為最小不平衡樹。

三、平衡二叉樹的平衡調整

定義

typedef enum Status{
    success = 0,//成功
    fail = 1//失敗
}Status;

//二叉樹結點定義
typedef struct BinaryTreeNode{
    int val;//存儲的值
    struct BinaryTreeNode *lChild,*rChild;//左右孩子
    int height;//深度
}BinaryTreeNode,*BinaryTree;

實現過程就是通過在一棵平衡二叉樹中一次插入結點(按照二叉排序樹的方式),若出現不平衡,則要根據新插入的結點與最低不平衡結點的位置關系進行相應調整。分為LL型、RR型、LR型和RL型四種類型,各調整方法如下(用A表示最低不平衡結點)。

1.LL型調整

LL型調整

在A的左孩子的左孩子上插入新節點,使得A的BF從1增到2,按照大小關系,結點B應作為新的根節點,其余兩個結點分別作為左右孩子結點才能平衡,A結點就好像是繞結點B順時針旋轉一樣。

具體算法步驟如下:
① 將A的左孩子B提升為新的根節點
② 將原來的根節點A降為B的右孩子
③ 各子樹按照大小關系連接(BL和AR不變,BR調整為A的左子樹)


image
/// LL型旋轉
/// @param node 原根節點
void ll_rotate(BinaryTree *node){
    BinaryTree A = *node;
    //取出B結點作為新的根節點
    BinaryTree B = A->lChild;
    //B的右子樹作為A的左子樹(因為B這邊都是比A小,所以只能為左子樹)
    A->lChild = B->rChild;
    //B的右子樹為A
    B->rChild = A;
    
    A->height = max(height(A->lChild), height(A->rChild)) + 1;
    B->height = max(height(B->lChild), height(B->rChild)) + 1;
    
    *node = B;
}

2.RR型調整

RR型調整

在A的右孩子B的右孩子上插入新結點C,使得A的平衡因子BF從-1變為-2,按照大小關系,結點B應作為新的根節點,其余兩個結點分別作為左右孩子結點才能平衡,A結點就好像是繞B逆時針旋轉一樣。

具體算法步驟如下:
① 將A的右孩子B提升為新的根節點
② 將原來的根節點A降為B的左孩子
③ 各子樹按照大小關系(AL和BR不變,將BL調整為A的右子樹)


image
/// rr型旋轉
/// @param node 原根節點
void rr_rotate(BinaryTree *node){
    BinaryTree A = *node;
    //取出B結點作為新的根節點
    BinaryTree B = A->rChild;
    //B的左子樹作為A的右子樹(因為B這邊都是比A大,所以只能為右子樹)
    A->rChild = B->lChild;
    //A作為B的左子樹
    B->lChild = A;
    
    A->height = max(height(A->lChild), height(A->rChild)) + 1;
    B->height = max(height(B->lChild), height(B->rChild)) + 1;
    
    *node = B;
}

3.LR型調整

LR型調整

在A的左孩子B的右孩子上插入新節點C,使得A的BF從1增大為2,按照大小關系,結點C應該作為新的根節點,其余兩個結點分別作為左右孩子結點才能平衡。

算法步驟如下:
① 將B的右孩子C提升為新的根節點
② 將原來的根節點A降為C的右孩子,B降為C的左孩子
③ 各子樹按照大小關系連接(BL和AR不變,CL和CR分別調整為B的右子樹和A的左子樹)


image
/// lr型旋轉
/// @param node 原根節點
void lr_rotate(BinaryTree *node){
    //先對B進行rr轉換,接著再來一次ll轉換
    rr_rotate(&((*node)->lChild));
    ll_rotate(node);
}

4.RL型調整

RL型調整

在A的右孩子B的左子樹上插入新節點C,此時A的BF由-1變為-2,按照大小關系,結點C應作為新的根節點,其余兩個結點分別作為左右孩子結點才能平衡。

算法步驟如下:
① 將B的左孩子C提升為新的根節點
② 將原來的根節點A降為C的左孩子,B降為C的右孩子
③ 各子樹按大小關系連接(AL和BR不變,CL和CR分別調整為A的左子樹和B的右子樹)


image
/// rl型轉換
/// @param node 原根節點
void rl_rotate(BinaryTree *node){
    //先對B進行ll型轉換,接著再來一次rr型轉換
    ll_rotate(&((*node)->rChild));
    rr_rotate(node);
}

四、插入代碼實現

BalanceBinaryTree.h中


#ifndef BalanceBinaryTree_h
#define BalanceBinaryTree_h

#include <stdio.h>
#include <stdlib.h>

typedef enum Status{
    success = 0,//成功
    fail = 1//失敗
}Status;

//二叉樹結點定義
typedef struct BinaryTreeNode{
    int val;//存儲的值
    struct BinaryTreeNode *lChild,*rChild;//左右孩子
    int height;//深度
}BinaryTreeNode,*BinaryTree;

Status insertVAL(BinaryTree *T,int key);
void preOrder(BinaryTree T);

#endif

BalanceBinaryTree.c中

#include "BalanceBinaryTree.h"

/// 返回高度
/// @param node 樹結點
int height(BinaryTree node){
    if (node == NULL) {
        return 0;
    }
    return node->height;
}

int max(int x, int y){
    return (x > y) ? x : y;
}
/// 生成一個新的結點
/// @param key 結點的值
BinaryTree newNode(int key){
    BinaryTree node = (BinaryTree)malloc(sizeof(BinaryTreeNode));
    node->val = key;
    node->lChild = node->rChild = 0;
    node->height = 1;
    return node;
}


/// LL型旋轉
/// @param node 原根節點
void ll_rotate(BinaryTree *node){
    BinaryTree A = *node;
    //取出B結點作為新的根節點
    BinaryTree B = A->lChild;
    //B的右子樹作為A的左子樹(因為B這邊都是比A小,所以只能為左子樹)
    A->lChild = B->rChild;
    //B的右子樹為A
    B->rChild = A;
    
    A->height = max(height(A->lChild), height(A->rChild)) + 1;
    B->height = max(height(B->lChild), height(B->rChild)) + 1;
    
    *node = B;
}

/// rr型旋轉
/// @param node 原根節點
void rr_rotate(BinaryTree *node){
    BinaryTree A = *node;
    //取出B結點作為新的根節點
    BinaryTree B = A->rChild;
    //B的左子樹作為A的右子樹(因為B這邊都是比A大,所以只能為右子樹)
    A->rChild = B->lChild;
    //A作為B的左子樹
    B->lChild = A;
    
    A->height = max(height(A->lChild), height(A->rChild)) + 1;
    B->height = max(height(B->lChild), height(B->rChild)) + 1;
    
    *node = B;
}

/// lr型旋轉
/// @param node 原根節點
void lr_rotate(BinaryTree *node){
    //先對B進行rr轉換,接著再來一次ll轉換
    rr_rotate(&((*node)->lChild));
    ll_rotate(node);
}

/// rl型轉換
/// @param node 原根節點
void rl_rotate(BinaryTree *node){
    //先對B進行ll型轉換,接著再來一次rr型轉換
    ll_rotate(&((*node)->rChild));
    rr_rotate(node);
}

/// 獲取結點的平衡因子
/// @param node 結點
int getBF(BinaryTree node){
    if (!node) {
        return 0;
    }
    return height(node->lChild) - height(node->rChild);
}

Status insertVAL(BinaryTree *T,int key){
    if (!*T) {
        //空樹,直接新建一個結點
        *T = newNode(key);
        return success;
    }
    if (key < (*T)->val) {
        //如果比結點的值小,從左子樹去找地方插入
        insertVAL(&((*T)->lChild), key);
    } else if (key > (*T)->val) {
        //如果比結點的值大,從右子樹去找地方插入
        insertVAL(&((*T)->rChild), key);
    } else {
        //相等因為沒有插入新結點,所以直接返回
//        return fail;
    }
    
    (*T)->height = 1 + max(height((*T)->lChild), height((*T)->rChild));
    
    int bf = getBF(*T);
    printf("%d,key:%d bf:%d\n",(*T)->val,key,bf);
    
    if (bf > 1 && key < (*T)->lChild->val) {
        //ll型
        ll_rotate(T);
    } else if (bf < -1 && key > (*T)->rChild->val){
        //rr型
        rr_rotate(T);
    } else if (bf > 1 && key > (*T)->lChild->val){
        //lr型
        lr_rotate(T);
    } else if (bf < -1 && key < (*T)->rChild->val){
        //rl型
        rl_rotate(T);
    }
    
    
    
    return success;
}

void preOrder(BinaryTree T){
    if (!T) {
        return;
    }
    printf("%d ",T->val);
    preOrder(T->lChild);
    preOrder(T->rChild);
}

我們進行測試,在main.c中:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "BalanceBinaryTree.h"

int main(int argc, const char * argv[]) {
    BinaryTree T = NULL;
    int arr[] = {2,10,3,5,6,1,7,9};
    for (int i = 0; i < 8; i++) {
        insertVAL(&T, arr[i]);
    }
    //前序遍歷
    preOrder(T);
    
    return 0;
}
/*
 打印結果:
 2,key:10 bf:-1
 10,key:3 bf:1
 2,key:3 bf:-2
 10,key:5 bf:1
 3,key:5 bf:-1
 5,key:6 bf:-1
 10,key:6 bf:2
 3,key:6 bf:-1
 2,key:1 bf:1
 3,key:1 bf:0
 10,key:7 bf:1
 6,key:7 bf:-1
 3,key:7 bf:-1
 7,key:9 bf:-1
 10,key:9 bf:2
 6,key:9 bf:-1
 3,key:9 bf:-1
 3 2 1 6 5 9 7 10 Program ended with exit code: 0
 */

參考自:
平衡二叉樹

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,501評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,673評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,610評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,939評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,668評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,004評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,001評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,173評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,705評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,426評論 3 359
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,656評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,139評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,833評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,247評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,580評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,371評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,621評論 2 380