都说平衡二叉树又臭又长,其实写起来不算长,不到200行,就是调试起来太麻烦了,熟练了之后应该会好点,今天整整调试了一下午,==变成了=,之后还有因为把插入新节点和修改平衡因子放在一个循环里面,后来发现插入路径上的有些节点的平衡因子不需要修改,结果调试了一下午 QwQ……
要注意的一点是AVL不是每插入一个节点就需要修改整个树的平衡因子,其实只需要修改在插入路径上最后一个平衡因子不为0的节点(后面称”修改节点”)的所有后代节点(因为平衡因子不为0的话,那么以它为根节点树肯定不是一颗满二叉树,那么就一定能通过旋转让他保持插入节点之前的深度,从而不会影响上面的节点)
旋转有四种,两种单旋转(左左,右右),两种双旋转(左右,右左),
1.头文件
struct node
{
int va, fb; //权,平衡因子
node *left, *right; //左右孩子
};
2.左左旋转
指插入的节点在修改节点的左孩子的左子树上,具体旋转看图.
具体代码如下:
node* llrotate(node *root) //左左
{
node *pa=root;
node *pb=root->left;
pa->left=pb->right;
pb->right=pa;
pa->fb=pb->fb=0;
return pb;
}
3.右右旋转
指插入的节点在修改节点的右孩子的右子树上,具体旋转看图.
具体代码如下:
node* rrrotate(node *root) //右右
{
node *pa=root;
node *pb=root->right;
pa->right=pb->left;
pb->left=pa;
pa->fb=pb->fb=0;
return pb;
}
4.左右旋转
指插入的节点在修改节点的左孩子的右子树上,这里和单旋转有点不同,因为需要旋转两次,所以要把修改节点的左孩子的右孩子表示出来,选以修改节点的左孩子为根做一次右右旋转,然后做一次左左旋转,,其实这里的插入有两种情况,插入节点可能在C(图)的左子树或者C的右子树,不过旋转方法是一样的只是修改平衡因子的时候有点不同,具体旋转看图.
具体代码如下:
node* lrrotate(node *root) //左右
{
node *pa=root;
node *pb=root->left;
node *pc=root->left->right;
pb->right=pc->left;
pc->left=pb;
pa->left=pc->right;
pc->right=pa;
if(pc->fb==1) //插入节点C的左子树上
{
pa->fb=-1;
pb->fb=pc->fb=0;
}
else if(pc->fb==-1) //插入节点在C的右子树上
{
pb->fb=1;
pa->fb=pc->fb=0;
}
else //插入节点就是C
pa->fb=pb->fb=pc->fb=0;
return pc;
}
5.右左旋转
指插入的节点在修改节点的右孩子的左子树上,这里和单旋转有点不同,因为需要旋转两次,所以要把修改节点的右孩子的左孩子表示出来,选以修改节点的左孩子为根做一次左左旋转,然后做一次右右旋转,,其实这里的插入有两种情况,插入节点可能在C(图)的左子树或者C的右子树,不过旋转方法是一样的只是修改平衡因子的时候有点不同,具体旋转看图.
具体代码如下:
node* rlrotate(node *root) //右左
{
node *pa=root;
node *pb=root->right;
node *pc=root->right->left;
pb->left=pc->right;
pc->right=pb;
pa->right=pc->left;
pc->left=pa;
if(pc->fb==1) //插入节点C的左子树上
{
pb->fb=-1;
pa->fb=pc->fb=0;
}
else if(pc->fb==-1)//插入节点C的左子树上
{
pa->fb=1;
pb->fb=pc->fb=0;
}
else //插入节点就是C
pa->fb=pb->fb=pc->fb=0;
return pc;
}
6.插入节点部分
插入的过程要保存修改节点 修改节点的父节点
node* Avl(node *head,int x)
{
node *newnode=new node;
newnode->fb=0;
newnode->va=x;
newnode->left=newnode->right=NULL;
if(!head) //空树
return newnode;
node *per,*fper,*flag,*fflag; //修改节点,修改节点的父节点,临时节点,临时节点的父节点
fper=fflag=NULL;
per=flag=head;
while(flag)
{
if(flag->fb!=0){ //可以做修改节点
per=flag; //保存
fper=fflag; //保存父节点方便旋转后连接旋转部分
}
fflag=flag; //保存父节点用于插入新节点
if(flag->va>x)
flag=flag->left;
else
flag=flag->right;
}
if(fflag->va>x) //插入新节点
fflag->left=newnode;
else
fflag->right=newnode;
flag=per;
while(flag!=newnode) //修改平衡因子,从修改节点开始,不可以和插入同步,因为插入时修改节点不确定
{
if(flag->va>x){ //定义左插平衡因子加一
flag->fb++;
flag=flag->left;
}
else{ //减一
flag->fb--;
flag=flag->right;
}
}
if(per->fb!=2&&per->fb!=-2) //树仍然保持平衡
return head;
else if(per->fb==2)//插入点在修改节点的左孩子的子树上
{
if(per->left->fb==1) //左子树
flag=llrotate(per);
else //右子树
flag=lrrotate(per);
}
else if(per->fb==-2)//插入点在修改节点的右孩子的子树上
{
if(per->right->fb==1) //左子树
flag=rlrotate(per);
else //右子树
flag=rrrotate(per);
}
if(!fper) //修改节点的父节点为空,表示修改节点是原树的根
return flag;
if(fper->left==per)
fper->left=flag;
else
fper->right=flag;
return head;
}