平衡二叉树是一种特化的二叉树,为纪念提出者Adelse-Velskil和Landis,因此也称AVL树。
为什么说它是特化的,特化体现在哪里呢?
平衡二叉树的平衡体现在,它的每个结点的左子树left和右子树right的高度height永远相差不超过1,换句话说,就是可以左边比右边高1,或者右边比左边高1,最好是两侧是同高(有利于检索数据时复杂度保持最低,从树另一个角度看是保证了两边吸收阳光光合作用营养均衡 :)
可以证明,由于平衡因子的存在,搜索复杂度保持在O(log N)。
那么我们首先可以直观地写出如下两个函数,getHeight和getBalance
int getHeight(结点)
{
return 结点的height;
}
int getBalance(左子树, 右子树)
{
return getHeight(左子树) - getHeight(右子树); //这个的绝对值后面要用来与1比大小
}
好了,那么我们的结点就很直观知道需要至少有这些结构成员:结点存放的数据值,结点的高度,结点所指的左子树,结点所指的右子树。
typedef struct node{
int value;
int height;
struct node*left; //左右子树用结构指针,递归定义左右子树的结点也有着同样的结构成员
struct node*right;
}node;
以上是C语言版本,若C++可以简化为以下:
struct node{
int value;
int height;
node*left;
node*right;
};
有了树的结构,那么我们来建立第一棵树,假设在树中存入的数据值value:
node*newNode(int value){
node*nNode = (node*)malloc(sizeof(node)); // 若C++赋值右边改为 = new node
nNode->value = value;
nNode->height = 1;
nNode->left = nNode->right = NULL;
return nNode;
}
这样我们可以把最开始定义的getHeight和getBalance更新为
int getHeight(node*node){
if(node==NULL) return 0;
else return node->height;
}
int getBalance(node*node){
return getHeight(node->left) - getHeight(node->right);
}
而树是会不断长高的(数据不断插入)那么我们也需要定义一个更新树高的函数,树高=从根结点root的左子树或右子树比较,取高的getHeight,然后加上根结点那段高度+1
int max(int a, int b){ //若C++可调用max不须写这个
return (a>b)?a:b;
}
int getUpdateHeight(node*node){
return 1+max(getHeight(node->left), getHeight(node->right));
}
那么,以上部分就是AVL的整体框架,接下来是针对它特化操作而准备的。为了保持平衡,在数据插入时很直观地可想到,如果每次直接从子树添加,会破坏本身平衡,也就是说,在数据插入时,结点之间的移位rotate是免不了的。
可以总结出来,在如一“撇”“丿"的形状下(即根-左子树L-左子树L)插入新数据,要保持或还原为 ” ^ "的形状,必须将上端往右下方弯折rightRotate。
同理,如一“捺” “\”的形状下(即根-右子树R-右子树R)插入新数据,要保持或还原为 ” ^ "的形状,必须将上端往左下方弯折leftRotate。
由此,我们可以写出LL型对应的rightRotate右旋;RR型对应的leftRotate左旋函数;
node* rightRotate(node*root){
node*newRoot = root->left; /* 原root的左子树担任新root,准备把原root弯下来作为新root的右子树,但是如果新root本身已经有右子树冲突怎么办(园艺上是重叠枝)要把它剪下来嫁接到原root做左子树(高度低了一层)如果有人说那原root如果也有左子树冲突了怎么办,没错原root肯定有左子树,但仔细发现已经不在了,在哪里?*/
root->left = newRoot->right; // 如newRoot无右子树即NULL也是可以的
newRoot->right = root;
root->height = getUpdateHeight(root); // 换位置后更新高度
newRoot->height = getUpdateHeight(newRoot);
return newRoot;
}
对应地,我们可以写出左旋
node* leftRotate(node*root){
node*newRoot = root->right;
root->right = newRoot->left;
newRoot->left = root;
root->height = getUpdateHeight(root);
newRoot->height = getUpdateHeight(newRoot);
return newRoot;
}
有了以上对数据的操作,接下来,就是综合起来做数据的插入