平衡二叉搜索樹(Self-balancing binary search tree)又被稱爲AVL樹(有別於AVL算法),且具有以下性質:它是一 棵空樹或它的左右兩個子樹的高度差的絕對值不超過1,並且左右兩個子樹都是一棵平衡二叉樹,同時,平衡二叉樹必定是二叉排序樹。
高度差可以用平衡因子bf來定義,我們用左子樹的高度減去右子樹的高度來表示bf,即-1<|bf|<1。
引入平衡二叉樹是由於二叉排序樹,在某些情況會導致樹的高度一直的增加,比如一組有序的數據,在查找或創建時遞歸層級會很深,導致方法棧容易溢出。
平衡二叉樹是通過旋轉來緩解樹高度增加過快的情況。
先介紹下最小不平衡節點的概念:插入一個節點之後,距離這個插入節點最近的不平衡節點就是最小不平衡節點。就是說在遞歸插入節點後,開始回溯,碰到的第一個不平衡的節點就是最小不平衡節點。
當最小不平衡節點右子樹高則需要左旋,左子樹高則需要右旋(還有些情況需要先對其左/右子樹旋轉)。
思考:
1、既然旋轉是通過平衡因子|bf|>1來決定怎麼旋轉的,那麼在旋轉前這些平衡因子是什麼時候賦值的呢?
2、旋轉之後,哪些節點需要調整?,平衡因子又改如何調整呢?
下圖只列出左子樹高的幾種情況,T表示最小不平衡節點,L表示其左子樹,LR表示L的右子樹,
爲了形象用EH(0),LH(1),RH(-1)分別表示某一節點 左右子樹相等、左子樹高、右子樹高三種情況。
根據L節點的左右子樹高度差來確定直接右旋還是先左旋再右旋,因爲L爲最小不平衡子樹的左子樹,故不會出現L.bf=EH的情況。
一、L.bf=LH
右旋:
旋轉之後T.bf=L.bf=EH
二、L.bf=RH
先左旋再右旋:
當L的平衡因子爲-1時則需要先對L進行右旋,然後再對T進行左旋。根據LR的情況再分爲下面三種(因爲旋轉兩次,那麼最後最小不平衡子樹的根節點爲LR,並且LR.bf=EH)
1、 LR=EH
旋轉之後T.bf=L.bf=EH
2、 LR=LH
旋轉之後T.bf=RH, L.bf=EH
3、 LR=RH
旋轉之後T.bf=EH, L.bf=LH
代碼如下:
#include<iostream>
#include<stdio.h>
#include<stdlib.h>
using namespace std;
typedef int Status;
constexpr auto LH = +1;
constexpr auto EH = 0;
constexpr auto RH = -1;
//定義平衡二叉樹的節點結構(在二叉排序樹中加入一個bf來存儲平衡因子)
typedef struct BiTNode
{
int data;
int bf;
struct BiTNode* lchild, * rchild;
}BiTNode, * BiTree;
void R_Rotate(BiTree* p);
void L_Rotate(BiTree* p);
void LeftBalance(BiTree *T);
void RightBalance(BiTree* T);
void InOrderTraverse(BiTree *T);
Status InsertAVL(BiTree *T,int e,Status *taller);
//進行二叉平衡樹時的右旋操作
void R_Rotate(BiTree *p)
{
BiTree L;
L = (*p)->lchild;
(*p)->lchild = L->rchild;
L->rchild = *p;
*p = L;
}
//進行二叉平衡樹時的左旋操作
void L_Rotate(BiTree *p)
{
BiTree R;
R = (*p)->rchild;
(*p)->rchild = R->lchild;
R->lchild = (*p);
*p = R;
}
//構建二叉平衡樹時左邊太“重”,進行左平衡處理函數
void LeftBalance(BiTree* T)
{
BiTree L, Lr;
L = (*T)->lchild;
switch (L->bf)
{/*檢查T的左子樹的平衡度,並做相應的處理*/
case LH:/*新節點插入在了T的左孩子的左子樹上,只要做單左旋處理*/
(*T)->bf = L->bf = EH;
R_Rotate(T);
break;
case RH:/*新節點插入在了T的左孩子的右子樹上,要做雙旋處理,
通過判斷T的左孩子的右子樹的根節點的bf來決定進行雙旋處理後各自的bf值*/
Lr = L->rchild;
switch (Lr->bf)
{
case LH:
(*T)->bf = RH;
L->bf = EH;
break;
case EH:
(*T)->bf = L->bf = EH;
break;
case RH:
(*T)->bf = EH;
L->bf = LH;
break;
}
Lr->bf = EH;
L_Rotate(&(*T)->lchild);
R_Rotate(T);
}
}
//構建二叉平衡樹時右邊太“重”,進行右平衡處理函數
void RightBalance(BiTree* T)
{
BiTree R, Rl;
R = (*T)->rchild;
switch (R->bf)
{/*檢查T的右子樹的平衡度,並做相應的處理*/
case RH:/*新節點插入在了T的右孩子的右子樹上,只要做單左旋處理*/
(*T)->bf = R->bf = EH;
L_Rotate(T);
break;
case LH:/*新節點插入在了T的右孩子的左子樹上,要做雙旋處理,
通過判斷T的右孩子的左子樹的根節點的bf來決定進行雙旋處理後各自的bf值*/
Rl = R->lchild;
switch (Rl->bf)
{
case LH:
(*T)->bf = EH;
R->bf = RH;
break;
case EH:
(*T)->bf = R->bf = EH;
break;
case RH:
(*T)->bf = LH;
R->bf = EH;
break;
}
Rl->bf = EH;
R_Rotate(&(*T)->rchild);
L_Rotate(T);
}
}
//構建二叉平衡樹函數
Status InsertAVL(BiTree* T, int e, Status* taller)
{
if (!*T)
{
*T = (BiTree)malloc(sizeof(BiTNode));
(*T)->data = e;
(*T)->bf = EH;
(*T)->lchild = (*T)->rchild = NULL;
*taller = true;
}
else
{
if (e==(*T)->data)
{
*taller = false;
return false;
}
if (e<(*T)->data)
{
int result = InsertAVL(&(*T)->lchild,e,taller);
if (!result)
{
return false;
}
if (*taller)
{
switch ((*T)->bf)
{
case LH:
LeftBalance(T);
*taller = false;
break;
case EH:
(*T)->bf = LH;
*taller = true;
break;
case RH:
(*T)->bf = EH;
*taller = false;
break;
}
}
}
else
{
int result = InsertAVL(&(*T)->rchild, e, taller);
if (!result)
{
return false;
}
if (*taller)
{
switch ((*T)->bf)
{
case LH:
(*T)->bf = EH;
*taller = false;
break;
case EH:
(*T)->bf = RH;
*taller = true;
break;
case RH:
RightBalance(T);
*taller = false;
break;
}
}
}
}
return true;
}
//二叉樹中序遍歷遞歸算法
void InOrderTraverse(BiTree* T)
{
if (*T==NULL)
{
return;
}
InOrderTraverse(&(*T)->lchild);
cout << (*T)->data << " ";
InOrderTraverse(&(*T)->rchild);
}
int main()
{
int i;
int a[10] = {3,2,1,4,5,6,7,10,9,8};
BiTree T = NULL;
Status Numtaller;
for ( i = 0; i < 10; i++)
{
InsertAVL(&T,a[i],&Numtaller);
}
//中序遍歷輸出結果
InOrderTraverse(&T);
}