什么是平衡二叉树?
对于二叉搜索树,节点的不同插入次序,将导致不同的深度和查找效率。这样就出现了一个问题。如何设计二叉树的结构可以让查找效率更高呢?如果一棵树它的左右子树都一样。既左右子树的高度和节点的数量都一样这样的树,查找效率就最高可是这个都一样的要求太高了。我们把标准再降低一点就有了平衡二叉树。
平衡因子:BF(T)=HL- HR;其中HL,HR分别代表数T的左右子树的高度。
平衡二叉树(AVL树):空树,或者左子树和右子树的高度差的绝对值不超过1,既| BF(T) |<=1.。
平衡二叉树的调整:
平衡二叉树在插入或删除一个节点后,可能会让二叉树失去平衡。根据不同的实际情况,
我们将二叉树的调整分为如下几种方法:
RR旋转:
实际情况:
如图所示左边的树插入节点C后,A节点的平衡因子变为-2.这时我们称不平衡的“发现者”是A节点,平衡的破坏者,既麻烦节点是C节点,它在“发现者”节点A的右孩子的右子树上。这种情况我们采用RR旋转得到右边的树。(RR旋转又称右单旋)
所有麻烦节点在发现者右孩子的右子树既需要(RR旋转)的情况可以概括为下图:
LL旋转:
如图所示:左树插入节点A后破坏了C节点的平衡。麻烦节点A,在发现者节点C的左孩子的左子树上
这种情况我们用LL旋转得到右边的树。(LL旋转又称左单旋)
所有麻烦节点在发现者的左孩子的左子树上既需要(LL旋转)的情况可以概括为下图:
LR旋转:
如图所示:左树插入节点C后破坏了E节点的平衡。麻烦节点C,在发现者节点E的左孩子的右子树上
这种情况我们用LR旋转得到右边的树。(LR旋转又称LR双旋,因为就上图而言;他是先以发现者节点E的左孩子B为根节点,做一次右单旋,再以E为根节点做一次左单旋形成的)
所有麻烦节点在发现者的左孩子的右子树上既需要(LR旋转)的情况可以概括为下图:
RL旋转:
如图所示:左树插入节点D后破坏了B节点的平衡。麻烦节点D,在发现者节点B的右孩子的左子树上
这种情况我们用RL旋转得到右边的树。(RL旋转又称 RL双旋,原因与LR双选类似)
所有麻烦节点在发现者的左孩子的右子树上既需要(RL旋转)的情况可以概括为下图:
平衡二叉树调整的实现:
#include<stdio.h>
#include<stdlib.h>
#define Elementype char
typedef struct AVLTree{//AVL树的结构
Elementype data;
struct AVLTree* L_child;
struct AVLTree* R_child;
int Hight;//树的高度
}AVLTree;
int Max ( int a, int b )
{
return a > b ? a : b;
}
int GetHight(AVLTree* A){//获取树的高度
if(!A){
return -1;
}else{
return A ->Hight;
}
}
AVLTree* SingleLeftRotation ( AVLTree* A ){//LL旋转(左单旋)
// 注意:A必须有一个左子结
// 将A与B做左单旋,更新A与B的高度,返回新的根结点B
AVLTree* B = A ->L_child;
A ->L_child = B ->R_child;
B ->R_child = A;
A ->Hight = Max(GetHight(A ->L_child) ,GetHight(A ->R_child)) + 1;
B ->Hight = A ->Hight + 1;
return B;
}
AVLTree* SinglRightRotation ( AVLTree* A ){//RR旋转(右单旋)
// 注意:A必须有一个右孩子
// 将A与B做右单旋,更新A与B的高度,返回新的根结点B
AVLTree* B = A ->R_child;
A ->R_child = B ->L_child;
B ->L_child = A;
A ->Hight = Max(GetHight(A ->L_child) ,GetHight(A ->R_child)) + 1;
B ->Hight = A ->Hight + 1;
return B;
}
AVLTree* LeftRightRotation ( AVLTree* A ){//LR旋转 (先以A -> L_child 做一次RR旋转,再以A为根节点做一次LL旋转)
// 注意:A必须有一个左孩子(B),B必须有一个右孩子(C)
AVLTree* B = A ->L_child;
A ->L_child = SinglRightRotation(B);
return SingleLeftRotation(A);
}
AVLTree* RightLeftRotation ( AVLTree* A ){//RL旋转(先以A ->R_child 做一次LL旋转,再以A为根节点做一次RR旋转)
// 注意:A必须有一个右孩子(B),B必须有一个左孩子(C)
AVLTree* B = A ->R_child;
A ->R_child = SingleLeftRotation(B);
return SinglRightRotation(A);
}
AVLTree* Insert( AVLTree* A , Elementype data ){//平衡树的插入操作
if(!A){
A = (AVLTree*)malloc(sizeof(AVLTree));
A ->data = data;
A ->Hight = 0;
A ->L_child = A ->R_child = NULL;
}else if(data < A ->data){//data 应插入A的左子树
A ->L_child = Insert( A ->L_child , data);
if(GetHight( A ->L_child ) - GetHight( A ->R_child) == 2){//插入后不平衡
if(data < A ->L_child ->data){//LL旋转
A = SingleLeftRotation(A);
}else{//LR旋转
A = LeftRightRotation(A);
}
}
}else if(data > A ->data){// data应插入A的右子树
A ->R_child = Insert(A ->R_child , data);
if( GetHight( A ->L_child ) - GetHight( A ->R_child ) == -2 ){
if( data > A ->R_child ->data ){//RR旋转
A = SinglRightRotation(A);
}else{//RL旋转
A = RightLeftRotation(A);
}
}
}
//更新树的高度
A ->Hight = Max( GetHight( A ->L_child) , GetHight( A ->R_child )) + 1;
return A;
}
void Pre_show_Tree(AVLTree* ptr){//先序遍历输出AVL树
if(ptr){
printf("%c ",ptr ->data);
Pre_show_Tree(ptr ->L_child);
Pre_show_Tree(ptr ->R_child);
}
}