图解数据结构树之AVL树
AVL树(平衡二叉树):
AVL树本质上是一颗二叉查找树,但是它又具有以下特点:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。在AVL树中任何节点的两个子树的高度最大差别为一,所以它也被称为平衡二叉树。下面是平衡二叉树和非平衡二叉树对比的例图:
平衡因子(bf):结点的左子树的深度减去右子树的深度,那么显然-1<=bf<=1;
AVL树的作用:


由上图可知,同样的结点,由于插入方式不同导致树的高度也有所不同。特别是在带插入结点个数很多且正序的情况下,会导致二叉树的高度是O(N),而AVL树就不会出现这种情况,树的高度始终是O(lgN).高度越小,对树的一些基本操作的时间复杂度就会越小。这也就是我们引入AVL树的原因
AVL树的基本操作:
AVL树的操作基本和二叉查找树一样,这里我们关注的是两个变化很大的操作:插入和删除!
我们知道,AVL树不仅是一颗二叉查找树,它还有其他的性质。如果我们按照一般的二叉查找树的插入方式可能会破坏AVL树的平衡性。同理,在删除的时候也有可能会破坏树的平衡性,所以我们要做一些特殊的处理,包括:单旋转和双旋转!
AVL树的插入,单旋转的第一种情况---右旋:
由上图可知:在插入之前树是一颗AVL树,而插入之后结点T的左右子树高度差的绝对值不再 < 1,此时AVL树的平衡性被破坏,我们要对其进行旋转。由上图可知我们是在结点T的左结点的左子树上做了插入元素的操作,我们称这种情况为左左情况,我们应该进行右旋转(只需旋转一次,故是单旋转)。具体旋转步骤是:
T向右旋转成为L的右结点,同时,Y放到T的左孩子上。这样即可得到一颗新的AVL树,旋转过程图如下:
左左情况的右旋举例:
AVL树的插入,单旋转的第一种情况---左旋:
由上图可知:在插入之前树是一颗AVL树,而插入之后结点T的左右子树高度差的绝对值不再 < 1,此时AVL树的平衡性被破坏,我们要对其进行旋转。由上图可知我们是在结点T的右结点的右子树上做了插入元素的操作,我们称这种情况为右右情况,我们应该进行左旋转(只需旋转一次,故事单旋转)。具体旋转步骤是:
T向右旋转成为R的左结点,同时,Y放到T的左孩子上。这样即可得到一颗新的AVL树,旋转过程图如下:
右右情况的左旋举例:
以上就是插入操作时的单旋转情况!我们要注意的是:谁是T谁是L,谁是R还有谁是X,Y,Z!T始终是开始不平衡的左右子树的根节点。显然L是T的左结点,R是T的右节点。X、Y、Y是子树当然也可以为NULL.NULL归NULL,但不能破坏插入时我上面所说的左左情况或者右右情况。
AVL树的插入,双旋转的第一种情况---左右(先左后右)旋:
由 上图可知,我们在T结点的左结点的右子树上插入一个元素时,会使得根为T的树的左右子树高度差的绝对值不再 < 1,如果只是进行简单的右旋,得到的树仍然是不平衡的。我们应该按照如下图所示进行二次旋转:
左右情况的左右旋转实例:
AVL树的插入,双旋转的第二种情况---右左(先右后左)旋:
由上图可知,我们在T结点的右结点的左子树上插入一个元素时,会使得根为T的树的左右子树高度差的绝对值不再 < 1,如果只是进行简单的左旋,得到的树仍然是不平衡的。我们应该按照如下图所示进行二次旋转:
右左情况的右左旋转实例:
AVL树的插入代码实现:(仅供参考)
懂了以上单旋转和双旋转的原理之后,那么代码写起来也就比较简单了,以下是我写的代码,如果有错还望大家不吝指正。(参考数据结构与算法分析-Weiss著)
#include <iostream> using namespace std; #define DataType int /*
定义AVL树的结构体,链式
*/
typedef struct AvlNode{
DataType data;
AvlNode * m_pLeft;
AvlNode * m_pRight;
int height;
}*AvlTree,*Position,AvlNode; //求两个数的最大值
int Max(int a,int b)
{
return a>b?a:b;
}
//求树的高度
int Height( AvlTree T)
{
if(NULL == T)
return -;
else
return T->height;
} //单旋转右旋
AvlTree singleRotateWithRight(AvlTree T)
{
AvlTree L = T->m_pLeft;
T->m_pLeft = L->m_pRight;
L->m_pRight = T;
T->height = Max( Height(T->m_pLeft),Height(T->m_pRight) ) + ;
L->height = Max( Height(L->m_pLeft),Height(L->m_pRight) ) + ;
return L; //此时L成为根节点了(可参考AVL的插入的左左情况的右旋图)
}
//单旋转左旋
AvlTree singleRotateWithLeft(AvlTree T)
{
AvlTree R = T->m_pRight;
T->m_pRight = R->m_pLeft;
R->m_pLeft = T;
T->height = Max( Height(T->m_pLeft),Height(T->m_pRight) ) + ;
R->height = Max( Height(R->m_pLeft),Height(R->m_pRight) ) + ;
return R; //此时R成为根节点了(可参考AVL的插入的左左情况的左旋图)
}
//双旋转,先左后右
AvlTree doubleRotateWithLeft(AvlTree T) //先左后右
{
T->m_pLeft = singleRotateWithLeft(T->m_pLeft);
return singleRotateWithRight(T);
}
//双旋转,先右后左
AvlTree doubleRotateWithRight(AvlTree T) //先右后左
{
T->m_pRight = singleRotateWithRight(T->m_pRight);
return singleRotateWithLeft(T);
}
AvlTree AvlTreeInsert(AvlTree T, DataType x)
{
if(T == NULL) //如果树为空
{
T = (AvlNode *)malloc(sizeof(struct AvlNode));
if(T)
{
T->data = x;
T->m_pLeft = NULL;
T->m_pRight = NULL;
T->height = ;
}
else
{
cout << "空间不够" << endl;
exit();
}
}
else if( x < T->data) //如果插入到T结点的左子树上
{
T->m_pLeft = AvlTreeInsert(T->m_pLeft,x); //先插入,后旋转
if(Height(T->m_pLeft) - Height(T->m_pRight) == ) //只有可能是这个
{
if(x < T->m_pLeft->data) //左左情况,只需要右旋转
{
T = singleRotateWithRight( T );
}
else //左右情况,双旋转,先左
{
T = doubleRotateWithLeft( T );
}
}
}
else if( x > T->data )
{
T->m_pRight = AvlTreeInsert(T->m_pRight,x);
if(Height(T->m_pRight) - Height(T->m_pLeft) == )
{
if(x > T->m_pRight->data) //右右情况,进行左旋
{
T = singleRotateWithLeft( T );
}
else //左右情况,双旋转,先右
{
T = doubleRotateWithRight( T );
}
}
}
//如果这个数已经存在,那么不进行插入
T->height = Max(Height(T->m_pLeft),Height(T->m_pRight)) + ;
return T;
}
//递归实现中序遍历
void inOrderVisitUseRecur(const AvlTree pCurrent)
{
if(pCurrent)
{
inOrderVisitUseRecur(pCurrent->m_pLeft);
cout << pCurrent->data << " ";
if(pCurrent->m_pLeft)
cout << " leftChild: "<<pCurrent->m_pLeft->data;
else
cout << " leftChild: "<<"NULL" ;
if(pCurrent->m_pRight)
cout << " rightChild: "<<pCurrent->m_pRight->data;
else
cout << " rightChild: "<< "NULL";
cout << endl;
inOrderVisitUseRecur(pCurrent->m_pRight);
}
}
int main()
{
AvlTree root = NULL;
root = AvlTreeInsert(root,);
root = AvlTreeInsert(root,);
root = AvlTreeInsert(root,);
root = AvlTreeInsert(root,);
root = AvlTreeInsert(root,);
root = AvlTreeInsert(root,);
root = AvlTreeInsert(root,);
root = AvlTreeInsert(root,);
root = AvlTreeInsert(root,);
root = AvlTreeInsert(root,);
root = AvlTreeInsert(root,);
root = AvlTreeInsert(root,);
root = AvlTreeInsert(root,);
root = AvlTreeInsert(root,);
root = AvlTreeInsert(root,);
inOrderVisitUseRecur(root);
return ;
}
图解数据结构树之AVL树的更多相关文章
- 【数据结构】平衡二叉树—AVL树
(百度百科)在计算机科学中,AVL树是最先发明的自平衡二叉查找树.在AVL树中任何节点的两个子树的高度最大差别为一,所以它也被称为高度平衡树.查找.插入和删除在平均和最坏情况下都是O(log n).增 ...
- 数据结构与算法——AVL树类的C++实现
关于AVL树的简单介绍能够參考:数据结构与算法--AVL树简单介绍 关于二叉搜索树(也称为二叉查找树)能够參考:数据结构与算法--二叉查找树类的C++实现 AVL-tree是一个"加上了额外 ...
- Mysql为什么使用b+树,而不是b树、AVL树或红黑树?
首先,我们应该考虑一个问题,数据库在磁盘中是怎样存储的?(答案写在下一篇文章中) b树.b+树.AVL树.红黑树的区别很大.虽然都可以提高搜索性能,但是作用方式不同. 通常文件和数据库都存储在磁盘,如 ...
- 数据结构(三)实现AVL树
AVL树的定义 一种自平衡二叉查找树,中面向内存的数据结构. 二叉搜索树T为AVL树的满足条件为: T是空树 T若不是空树,则TL.TR都是AVL树,且|HL-HR| <= 1 (节点的左子树高 ...
- 数据结构实验7:实现二分查找、二叉排序(查找)树和AVL树
实验7 学号: 姓名: 专业: 7.1实验目的 (1) 掌握顺序表的查找方法,尤其是二分查找方法. (2) 掌握二叉排序树的建立及查找. 查找是软件设计中的最常用的运算,查找所涉及到 ...
- 数据结构与算法分析-AVL树
1.AVL树是带有平衡条件的二叉查找树. 2.AVL树的每个节点高度最多相差1. 3.AVL树实现的难点在于插入或删除操作.由于插入和删除都有可能破坏AVL树高度最多相差1的特性,所以当特性被破坏时需 ...
- 数据结构——二叉查找树、AVL树
二叉查找树:由于二叉查找树建树的过程即为插入的过程,所以其中序遍历一定为升序排列! 插入:直接插入,插入后一定为根节点 查找:直接查找 删除:叶子节点直接删除,有一个孩子的节点删除后将孩子节点接入到父 ...
- 数据结构树之AVL树(平衡二叉树)
一 什么是AVL树(平衡二叉树): AVL树本质上是一颗二叉查找树,但是它又具有以下特点:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树.在AVL树中任何节 ...
- "《算法导论》之‘树’":AVL树
本文关于AVL树的介绍引自博文AVL树(二)之 C++的实现,与二叉查找树相同的部分则不作介绍直接引用:代码实现是在本文的基础上自己实现且继承自上一篇博文二叉查找树. 1.AVL树的介绍 AVL树是高 ...
随机推荐
- CSS---网络编程
CSS概述 CSS是层叠样式表(Cascading Style Sheets)用来定义网页的显示效果.可以解决html代码对样式定义的重复,提高了后期样式代码的可维护性,并增强了网页的显示效果功能.简 ...
- 格式化URL
//格式化url查询参数为json function formatUrl(url){ var reg=/(?:[?&]+)([^&]+)=([^&]+)/g; var data ...
- use isSubstring to check if one word is a rotation of another.
1: /// <summary> 2: /// Assume you have a method isSubstring which checks if one word is a s ...
- Write an algorithm such that if an element in an MxN matrix is 0, its entire row and column is set to 0.
1: /// <summary> 2: /// Write an algorithm such that if an element in an MxN matrix is 0, it ...
- Git 使用相关
GIT命令和使用 安装git完成后,创建工程目录,右键运行bash here 1.查看git当前配置信息: git config -l 2.配置用户名.邮箱git config --global us ...
- springMVC源码浅析
因故,需要学习springmvc,现在demo已经跑起来.列文分析springmvc并简单对比struts2. springmvc的核心对象dispatcherServlet.struts2的核心对象 ...
- python的import与from...import的不同之处
在python用import或者from...import来导入相应的模块.模块其实就是一些函数和类的集合文件,它能实现一些相应的功能,当我们需要使用这些功能的时候,直接把相应的模块导入到我们的程序中 ...
- ASP.NET MVC- MvcPager
本文目标 一.能够使用MvcPager进行分页 本文目录 一.MvcPager控件的简单使用 二.C#扩展方法 一.MvcPager控件的简单使用 1.添加MvcPager.dll的引用[下载] 2. ...
- jquery validate.js表单验证的基本用法入门
这里转载一篇前辈写的文章,在我自己的理解上修改了一下,仅作记录. 先贴一个国内某大公司的代码: 复制代码 代码如下: <script type="text/javascript&quo ...
- 使用Inputstream读取文件
在java中,能够使用InputStream对文件进行读取,就是字节流的输入.当读取文件内容进程序时,须要使用一个byte数组来进行存储,如此会有例如以下两个问题: 1.怎样建立合适大小的byte数组 ...