一、概述
建一棵AVL树。
代码挺复杂的,我调了一下午,因为有一个错误和一个段错误。
实际上最后发现错误就是一个本应是=的地方我打成了==。
一切需要复制黏贴的地方,都容易出错。
简直气死。
二、分析
首先,我们要明确这一点,AVL树是BST进化而来的。所以建AVL树也和建BST树大体上差不多。
一般插入法建树我们使用引用法,即声明一个insert函数,变量是node*&和int,把int作为节点插到root上。
先判断,如果是空的那就直接插,如果不是空的,小于往左插大于等于往右插。
建AVL树的框架也是这样。
但是多了几步:因为AVL树要求两子树高度相差不超过一。
求高度的函数如下:
int getHeight(node* root)
{
if(root==NULL)
return 0;
return root->height;
}
因此在插完之后,首先更新所插节点的高度。
如何更新高度呢?函数如下:
void updateHeight(node* root)
{
root->height=max(getHeight(root->lchild),getHeight(root->rchild))+1;
}
无返回值,参数为node*。
直接令root高度为两子树高度最大的那个加一。
然后判断root的平衡因子是否合法。要判断就要知道root的平衡因子,如下:
int getBalanceFactor(node* root)
{
return getHeight(root->lchild)-getHeight(root->rchild);
}
平衡因子就是左子树高度减去右子树高度。
然后把所有情况分为以下几种:
合法的:平衡因子等于0,1,-1;
不合法的:平衡因子等于2,-2。
这里注意一点,只有往左子树插,才可能出现2,往右子树插,才可能出现-2。
因此在左子树插完,判断是否为2即可。
如果是2,说明不再平衡,这里又有两种情况:
左子树的平衡因子为1或-1。
当平衡因子为1时,说明是LL型,直接将根节点右旋一次;
当平衡因子为-1时,说明是LR型,将左子树左旋,再将根节点右旋。
那么,左旋和右旋分别怎么写呢?如下:
//左旋
//左旋是让根节点的右子树当根节点
void L(node* &root)
{
node* temp=root->rchild;
root->rchild=temp->lchild;
temp->lchild=root;
updateHeight(root);
updateHeight(temp);
root=temp;
}
//右旋
//右旋是让根节点的左子树当根节点
void R(node* &root)
{
node* temp=root->lchild;
root->lchild=temp->rchild;
temp->rchild=root;
updateHeight(root);
updateHeight(temp);
root=temp;
}
这最好用笔画一下,左旋就是(右子树)往左跳,右旋就是(左子树)往右跳。
注意返回值为空,参数要引用。
当平衡因子为-2时也类似。
于是树就建好了。
三、总结
建AVL树的模板最好记下来。注意节点的平衡因子不用保存,而高度需要保存。
总结起来三大基础函数(求节点高度,求平衡因子,更新高度),两大功能函数(左旋,右旋),一大插入函数。
PS:代码如下:
#include<cstdio>
#include<algorithm>
using namespace std;
struct node
{
int v,height;
node* lchild,*rchild;
}*root;
//获得当前节点的高度
int getHeight(node* root)
{
if(root==NULL)
return 0;
return root->height;
}
//更新当前节点高度
void updateHeight(node* root)
{
root->height=max(getHeight(root->lchild),getHeight(root->rchild))+1;
}
//获得当前节点的平衡因子
int getBalanceFactor(node* root)
{
return getHeight(root->lchild)-getHeight(root->rchild);
}
//左旋
//左旋是让根节点的右子树当根节点
void L(node* &root)
{
node* temp=root->rchild;
root->rchild=temp->lchild;
temp->lchild=root;
updateHeight(root);
updateHeight(temp);
root=temp;
}
//右旋
//右旋是让根节点的左子树当根节点
void R(node* &root)
{
node* temp=root->lchild;
root->lchild=temp->rchild;
temp->rchild=root;
updateHeight(root);
updateHeight(temp);
root=temp;
}
node* newnode(int a)
{
node* root=new node;
root->v=a;
root->height=1;
root->lchild=root->rchild=NULL;
return root;
}
void insert(node* &root,int a)
{
if(root==NULL)
{
root=newnode(a);
return;
}
else
{
if(a<root->v)
{
insert(root->lchild,a);//往左子树插
updateHeight(root);
if(getBalanceFactor(root)==2)//不平衡,那一定是根节点在往左子树查之后平衡因子变为2
{
if(getBalanceFactor(root->lchild)==1)//左子树的平衡因子为一,说明插到LL
{
R(root);//对根节点进行右转
}
else if(getBalanceFactor(root->lchild)==-1)//左子树平衡因子为负一,说明插到LR
{
L(root->lchild);//首先左转,把LR变成LL
R(root);//然后按LL做
}
}
}
else
{
insert(root->rchild,a);
updateHeight(root);
if(getBalanceFactor(root)==-2)//不平衡,那一定是根节点往右子树插之后平衡因子变为-2
{
if(getBalanceFactor(root->rchild)==-1)//右子树平衡因子为负一,说明插到RR
{
L(root);//对根节点进行左转
}
else if(getBalanceFactor(root->rchild)==1)//右子树平衡节点为一,说明插到RL
{
R(root->rchild);//首先右转,把RL变为RR
L(root);//然后按RR做
}
}
}
}
}
int main()
{
int n,v;
scanf("%d",&n);
for(int i=0;i<n;i++)
{
scanf("%d",&v);
insert(root,v);
}
int ans=root->v;
printf("%d\n",ans);
}