如果二叉搜索树遇到了极端情况,也就是数组本身就有序,做成BST树就会退化成一个链表,查询时间大大增加。因此需要优化,让我们制作BST树时,能让数值均匀的分布在二叉树的左右。
为了让二叉树平衡,引进了一个平衡因子balance,表示右子树与左子树的高度差,这个平衡因子只有-1,0,1三种取值,-1表示左边比右边高一,0表示平衡,1同理。当balance取其他值时,这个二叉树就不平衡了,因此要做出调整。
调整的方法为左右旋转以及双旋转,下面将逐一讲解。
先写出定义
typedef int ElemType;
typedef struct AVLNode //BinarySortTreeNode
{
struct AVLNode* leftchild;
struct AVLNode* parent;
struct AVLNode* rightchild;
int balance; // 0 // 1 -1
ElemType data;
}AVLNode, * PAVLNode;
typedef struct
{
AVLNode* root;
int cursize;
}AVLTree;
购买节点
AVLNode* Buynode(ElemType val)
{
AVLNode* s = (AVLNode*)malloc(sizeof(AVLNode));
if (NULL == s) exit(EXIT_FAILURE);
s->data = val;
s->leftchild = NULL;
s->parent = NULL;
s->rightchild = NULL;
s->balance=0;
return s;
}
例如下图中,12节点的balance为2,二叉树不平衡了,如果想让这个树平衡,同时还满足二叉搜索树,也就是中序遍历仍然有序,需要将23作为后根节点,12,34分别作为左右子树。
也就是不平衡的树是一条斜线。
这种旋转称为左旋转。
逻辑很简单,就是指针的变换
1.将23作为新的根节点newroot
2.23若原有左孩子,就将其赋给12的右孩子(一定大于12)
3.23的左孩子leftchild指向12
4.如果12是根节点,那么23就替换成为根节点
5.如果12不是根节点,那么判断12是左孩子还是右孩子,然后将23替换。
6.双亲节点补全。
代码如下:
void RotateLeft(AVLTree* ptree,AVLNode* ptr){//左旋转
assert(ptree != NULL && ptr!=NULL);
AVLNode* newroot=ptr->rightchild;//定义新根节点
newroot->parent = ptr->parent;//新根节点双亲
ptr->rightchild = newroot->leftchild;//变换节点的子节点
if(newroot->leftchild!=NULL){
newroot->leftchild->parent = ptr;
}//如果新根节点没有子节点,那么直接改变
newroot->leftchild = ptr;
if(ptree->root == ptr){
ptree->root = newroot;//12若为根节点,newroot直接做根节点
}else{
if(ptr->parent->leftchild == ptr){
ptr->parent->leftchild == newroot;
}else{
ptr->parent->rightchild =newroot;
}
}//判断12是左子树还是右子树,然后变换。
ptr->parent = newroot;//3
}
右旋转同理,全部镜像改变
void RotateRight(AVLTree* ptree,AVLNode* ptr){
assert(ptree!=NULL && ptr!= NULL);
AVLNode* newroot = ptr->leftchild;
newroot->parent = ptr->parent;
ptr->leftchild = newroot->rightchild;
if(newroot->rightchild != NULL){
newroot->rightchild->parent =ptr;
}
newroot->rightchild = ptr;
if(NULL == ptr->parent){
ptree->root = newroot;
}else{
if(ptr->parent->leftchild == ptr){
ptr->parent->leftchild = newroot;
}else{
ptr->parent->rightchild=newroot;
}
}
ptr->parent = newroot;
}
当不平衡的区域是一条斜线是,用左右旋转解决不了,就要用到双旋转。
如下:
原理就是先用一次单旋转,将区域转化成斜线,然后再用一次旋转使之平衡
void LeftBalance(AVLTree* ptree, AVLNode* ptr)
{
assert(ptree != NULL && ptr != NULL);
AVLNode* leftsub = ptr->leftchild, * rightsub = NULL;
switch (leftsub->balance) // 0 1 -1;
{
case 0: printf("left balance \n"); break;
case -1://表示左大于右
ptr->balance = 0;
leftsub->balance = 0;
RotateRight(ptree, ptr);
break;
case 1://表示右大于左
rightsub = leftsub->rightchild;
switch (rightsub->balance)
{
case 0:
break;
case 1:
ptr->balance = 0;
leftsub->balance = -1;
break;
case -1:
ptr->balance = 1;
leftsub->balance = 0;
break;
}
rightsub->balance = 0;
RotateLeft(ptree,ptr->leftchild);
RotateRight(ptree,ptr);
break;
}
}
右旋转同理,镜像转换。