关于平衡二叉树(代码的实现以及理解)
刚刚开始学平衡二叉树,如果哪里有错的地方,还请指出来以更正。(哇~刚刷完二叉树心态崩了有木有,自闭了自闭了)
什么是平衡二叉树
- 定义: 平衡二叉搜索树又被称为AVL树,且具有以下性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。
- 作用: 我们知道,对于一般的二叉搜索树,其期望高度(即为一棵平衡树时)为log2n,其各操作的时间复杂度 (O(log2n) 同时也由此而决定。但是,在某些极端的情况下(如在插入的序列是有序的时),二叉搜索树将退化成近似链或链,此时,其操作的时间复杂度将退化成线性的,即O(n)。我们可以通过随机化建立二叉搜索树来尽量的避免这种情况,但是在进行了多次的操作之后,由于在删除时,我们总是选择将待删除节点的后继代替它本身,这样就会造成总是右边的节点数目减少,以至于树向左偏沉。这同时也会造成树的平衡性受到破坏,提高它的操作的时间复杂度。
在平衡二叉搜索树中,我们可以看到,其高度一般都良好地维持在O(log(n)),大大降低了操作的时间复杂度。(感谢度娘) - 如何去维护一颗平衡二叉树: 我们需要做的操作就是 当一个结点的左右子树高度(深度)差绝对值超过一时(拿上图来说,第一颗二叉树在3和5这两个结点都失衡了,第三颗二叉树则在7这个结点失衡),需要对它进行旋转(左旋或右旋)。
- 类型及具体操作:
LJF学长的手迹:
LDQ学长推荐的网站:平衡二叉树动图。
①LL型:由于在结点的左孩子的左子树上插入结点导致不平衡,需要进行右旋。
struct tree *LL(struct tree *root) //LL型右旋
{
struct tree *q;
q=root->l;
root->l=q->r;
q->r=root;
q->de=max(depth(q->l),depth(q->r))+1;
root->de=max(depth(root->l),depth(root->r))+1;
return q;
}
②RR型:由于在结点的右孩子的右子树上插入结点导致不平衡,需要进行左旋。
struct tree *RR(struct tree *root) //RR型左旋
{
struct tree *q;
q=root->r;
root->r=q->l;
q->l=root;
q->de=max(depth(q->l),depth(q->r))+1;
root->de=max(depth(root->l),depth(root->r))+1;
return q;
}
③LR型:
struct tree *LR(struct tree *root) //LR型 先将其左子树左旋变为LL型再
{ //进行LL型的操作(右旋)
root->l=RR(root->l);
return LL(root);
}
④RL型
struct tree *RL(struct tree *root) //RL型 先将其右子树右旋变为RR型再
{ //进行RR的操作(左旋)
root->r=LL(root->r);
return RR(root);
}
现在来具体看一个题目
Problem Description
根据给定的输入序列建立一棵平衡二叉树,求出建立的平衡二叉树的树根。
Input
输入一组测试数据。数据的第1行给出一个正整数N(n <= 20),N表示输入序列的元素个数;第2行给出N个正整数,按数据给定顺序建立平衡二叉树。
Output
输出平衡二叉树的树根。
Sample Input
5
88 70 61 96 120
Sample Output
70
直接上码
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
struct tree
{
int data;
struct tree *l,*r;
int de; //因为需要用到每个节点的深度
};
int max(int a,int b)
{
if(a>b)
return a;
else
return b;
}
int depth(struct tree *root) //如果root指向为空则返回-1,否则直接返回其深度
{
if(root==NULL)
return -1;
else
return root->de;
}
struct tree *LL(struct tree *root) //LL型右旋
{
struct tree *q;
q=root->l;
root->l=q->r;
q->r=root;
q->de=max(depth(q->l),depth(q->r))+1;
root->de=max(depth(root->l),depth(root->r))+1;
return q;
}
struct tree *RR(struct tree *root) //RR型左旋
{
struct tree *q;
q=root->r;
root->r=q->l;
q->l=root;
q->de=max(depth(q->l),depth(q->r))+1;
root->de=max(depth(root->l),depth(root->r))+1;
return q;
}
struct tree *LR(struct tree *root) //LR型 先将其左子树左旋变为LL型再进行LL型的操作(右旋)
{
root->l=RR(root->l);
return LL(root);
}
struct tree *RL(struct tree *root) //RL型 现将其右子树右旋变为RR型再进行RR的操作(左旋)
{
root->r=LL(root->r);
return RR(root);
}
struct tree * in(struct tree *root,int key) //建树
{
if(root==NULL)
{
root=(struct tree *)malloc(sizeof(struct tree ));
root->data=key;
root->l=NULL;
root->r=NULL;
root->de=0;
return root;
}
if(key<root->data)
{
root->l=in(root->l,key);
if(depth(root->l)-depth(root->r)>1)
{
if(key<root->l->data) //值得注意的是这个小细节,这里比较的是key的值与结点左子树的值
root=LL(root); //因为在此之前我们已经确定了是在结点的左孩子上插入,所以此处这样写用以判断是LR还是LL
else
root=LR(root);
}
}
else
{
root->r=in(root->r,key);
if(depth(root->r)-depth(root->l)>1)
{
if(key>root->r->data) //这里是同样的道理
root=RR(root);
else
root=RL(root);
}
}
root->de=max(depth(root->l),depth(root->r))+1; //至于这里为什么要取+1就不做解释,可以自己动手画一画
return root;
}
int main()
{
int n,i,m;
struct tree *root;
scanf("%d",&n);
root=NULL;
for(i=1; i<=n; i++)
{
scanf("%d",&m);
root=in(root,m);
}
printf("%d\n",root->data);
return 0;
}
刚开始接触二叉树还是比较难的,上面这个代码改了好久,就是因为上面说的那个小细节没处理好,所以在这里把平衡二叉树重新梳理一遍。