avl树的c实现
2015-10-15 21:48
323 查看
一.简介:一看到AVL树,首先联想到岛国动作片有木有
,不过AVL树是得名于它的发明者 G.M. Adelson-Velsky 和 E.M. Landis。
AVL树其实是平衡二叉树的限定版,
二.性质
(1).不仅仅具有平衡二叉树的所有性质(注:平衡二叉树性质:左节点的值<父节点的值<右节点的值)
(2).每个节点左右子树高度的差最多为1。这是什么意思呢?给个例子大家就明白了
如: (a) (b)
5 5
/ \ / \
2 8 2 8
/ \ / / \
1 4 7 1 4
/ /
3 3
两者的区别显而易见,(b)树的节点要少一个7。
对于(a)树:5节点的左子树高度为3,右子树的高度为2.相差为1。
2节点的左子树高度为2,右子树的高度为1.相差为1
8节点的左子树高度为1,右子树的高度为0.相差为1.
以此类推所有节点的左右子树的高度差都小于等于1.
所有(a)树是avl树
对于(b)树:明显5节点左子树高度为3,右子树高度为1,相差为2>1
所以不是avl树
其实判断是avl树比较麻烦,因为每个节点都要看,不过判断不是的话就比较简单了,比较容易可以看出,不过这些都是浮云啦,
通过下面的程序,大家就会知道电脑完全可以帮助我们作出判断。
三.AVL树的作用:
还是举个栗子~
1
\
2
\
3
\
4
是不是很不美观,完全没有了树的美感~~,但关键是增加了时间的复杂度,而我AVL大法就不会发生这样的事情了
AVL解决这个问题就是用了一个办法:旋转!
四。旋转:
1.单选转:
左边为调整前得节点,我们可以看出k2的左右子树已不再满足AVL平衡条件,调整后的为右图。
细的就不讲了一切尽在注释中
2.双旋转:
这种用单旋转显然不可以:
(1)
(2)
这里我们将图(1)的Y子树看成如图(2),以k2为子树根节点的树,我们将其子树分成比D低,这里我我先对k3的左子树进行一次情形四的右旋转,然后在进行一次情形1的左旋转
废话不多说了,直接撸代码,嘻嘻~~:
10
/ \
5 13
/ \
1 7
/ \
6 8
愉快撸码,快乐人生。
今天的教程就到这里了,大家学会了吗?
有什么建议欢迎在下方留言哦!
,不过AVL树是得名于它的发明者 G.M. Adelson-Velsky 和 E.M. Landis。
AVL树其实是平衡二叉树的限定版,
二.性质
(1).不仅仅具有平衡二叉树的所有性质(注:平衡二叉树性质:左节点的值<父节点的值<右节点的值)
(2).每个节点左右子树高度的差最多为1。这是什么意思呢?给个例子大家就明白了
如: (a) (b)
5 5
/ \ / \
2 8 2 8
/ \ / / \
1 4 7 1 4
/ /
3 3
两者的区别显而易见,(b)树的节点要少一个7。
对于(a)树:5节点的左子树高度为3,右子树的高度为2.相差为1。
2节点的左子树高度为2,右子树的高度为1.相差为1
8节点的左子树高度为1,右子树的高度为0.相差为1.
以此类推所有节点的左右子树的高度差都小于等于1.
所有(a)树是avl树
对于(b)树:明显5节点左子树高度为3,右子树高度为1,相差为2>1
所以不是avl树
其实判断是avl树比较麻烦,因为每个节点都要看,不过判断不是的话就比较简单了,比较容易可以看出,不过这些都是浮云啦,
通过下面的程序,大家就会知道电脑完全可以帮助我们作出判断。
三.AVL树的作用:
还是举个栗子~
1
\
2
\
3
\
4
是不是很不美观,完全没有了树的美感~~,但关键是增加了时间的复杂度,而我AVL大法就不会发生这样的事情了
AVL解决这个问题就是用了一个办法:旋转!
四。旋转:
1.单选转:
左边为调整前得节点,我们可以看出k2的左右子树已不再满足AVL平衡条件,调整后的为右图。
细的就不讲了一切尽在注释中
//左单选转,K2是根节点,通过旋转K1成为根节点,K2成为K1的右子树 static Position SingleRotatewithLeft(Position K2) { Position K1; //K1为K2的左子树 K1=K2->Left; //K1的右子树成为K2的左子树 K2->Left=K1->Right; //K2成为K1的右子树。 //K1成为根,K1的右子树不变,左子树是K2。 //K2的左子树不变,右子树是K1的左子树 K1->Right=K2; return K1; }
2.双旋转:
这种用单旋转显然不可以:
(1)
(2)
这里我们将图(1)的Y子树看成如图(2),以k2为子树根节点的树,我们将其子树分成比D低,这里我我先对k3的左子树进行一次情形四的右旋转,然后在进行一次情形1的左旋转
//左双旋转,左边深度大,通过一次右单旋转,一次左单选转 static Position DoubleRotatewithLeft(Position P3) { P3->Left=SingleRotatewithRight(P3->Left); return P3=SingleRotatewithLeft(P3); }其实很简单的有木有,看似复杂的东西代码不过5行
废话不多说了,直接撸代码,嘻嘻~~:
10
/ \
5 13
/ \
1 7
/ \
6 8
#include<stdio.h>把上面的树实现一下:开始可以先不写入6,把6当作插入,结果如下图
#include<stdlib.h>
//进行求高度时需要
#define Max(a,b) ((a>b)?a:b)
typedef int ElementType;
typedef struct AvlNode
{
ElementType Element;
struct AvlNode * Left;
struct AvlNode * Right;
int height;//高度
}*Position,Node;
static int Height(Position P)
{
if(P==NULL)
return -1;
else
return 1+Max(Height(P->Left),Height(P->Right));
}
//左单选转,P2是根节点,通过旋转P1成为根节点,P2成为P1的右子树
static Position SingleRotatewithLeft(Position P2)
{
Position P1;
//让P1为P2的左子树
P1=P2->Left;
//P1的右子树成为P2的左子树
P2->Left=P1->Right;
//P2成为P1的右子树。
//P1成为根,P1的右子树不变,左子树是P2。
//P2的左子树不变,右子树是P1的左子树
P1->Right=P2;
P2->height=Max(Height(P2->Left),Height(P2->Right))+1;
P1->height=Max(Height(P1->Left),Height(P1->Right))+1;
return P1;
}
static Position SingleRotatewithRight(Position P2)
{
Position P1;
P1=P2->Right;
P2->Right=P1->Left;
P1->Left=P2;
P2->height=Max(Height(P2->Left),Height(P2->Right))+1;
P1->height=Max(Height(P1->Left),Height(P1->Right))+1;
return P1;
}
//左双旋转,左边深度大,通过一次右单旋转,一次左单选转 static Position DoubleRotatewithLeft(Position P3) { P3->Left=SingleRotatewithRight(P3->Left); return P3=SingleRotatewithLeft(P3); }
static Position DoubleRotatewithRight(Position P3)
{
P3->Right=SingleRotatewithLeft(P3->Right);
return P3=SingleRotatewithRight(P3);
}
//插入Element为X的节点
Position Insert(ElementType X,Position *root)
{
//当遍历到节点为NULL时,为该节点申请内存并赋值X
if(*root==NULL){
*root=(Position)malloc(sizeof(struct AvlNode *));
//增强健壮性
if(*root==NULL)
printf("out of space!");
else{
(*root)->Element=X;
(*root)->Left=NULL;
(*root)->Right=NULL;
}
}
//如果X比当前节点的值要大,那么就向右递归遍历
else if(X>(*root)->Element){
(*root)->Right=Insert(X,&(*root)->Right);
//这时已经找到节点并插入,需要做的就是判断是否进行旋转
//当*root的右子树的高度比左子树高度大2时说明需要旋转
if(Height((*root)->Right)-Height((*root)->Left)==2){
//如果插入节点大于根的右儿子的值,说明右边要深,所以进行单旋转
//否则说明右节点的左子树要深,所以双旋转
if((*root)->Right->Element>X)
*root=DoubleRotatewithRight(*root);
else
*root=SingleRotatewithLeft(*root);
}
}
else if(X<(*root)->Element){
(*root)->Left=Insert(X,&(*root)->Left);
if(Height((*root)->Left)-Height((*root)->Right)==2){
if((*root)->Left->Element<X)
*root=DoubleRotatewithLeft(*root);
else
*root=SingleRotatewithRight(*root);
}
}
(*root)->height=Max(Height((*root)->Left),Height((*root)->Right))+1;
return (*root);
}
//递归输入树,如果是0就相当于为空节点
void Input(Position *P)
{
int i;
scanf("%d",&i);
if(i!=0){
*P=malloc(sizeof(struct AvlNode *));
(*P)->Element=i;
Input(&(*P)->Left);
Input(&(*P)->Right);
}
else
(*P)=NULL;
}
//递归输出
void Print(Position root)
{
if(root){
printf("%5d",root->Element);
Print(root->Left);
Print(root->Right);
}
}
int main()
{
int i;
Position root=NULL;
printf("输入树:\n");
Input(&root);
printf("\n");
printf("输出树:\n");
Print(root);
printf("\n");
printf("Input");
scanf("%d",&i);
root=Insert(i,&root);
Print(root);
return 0;
}
愉快撸码,快乐人生。
今天的教程就到这里了,大家学会了吗?
有什么建议欢迎在下方留言哦!
相关文章推荐
- grub的三种安装方式
- 课上重点整理
- Linux文件系统以及目录结构简介
- c++primer——模板,标准库特殊设施
- android学习2:Intent与Bundle
- Lesson01-Homework UIView作业
- 第十章练习题----2
- lintcode-买卖股票的最佳时机II-150
- 12、第十二节课,css伪类 (转)
- linux lvm
- s3c2440裸机开发调试环境(MDK4.6,Jlink v8,mini2440)
- xwindow
- Linux中网络字节序和主机字节序
- Java值传递还是引用传递?
- Cgroup源码级别深入
- poj--1700
- request.getParameterValues()用法
- Tomcat安全配置与性能优化
- EasyUI 关于 panel,window,dialog 通过href加载页面,页面中引用的js不执行的解决方案
- POJ2947 Widget Factory 高斯消元+扩展GCD解同余方程组