平衡二叉查找树 AVL 的实现
不同结构的二叉查找树,查找效率有很大的不同(单支树结构的查找效率退化成了顺序查找)。如何解决这个问题呢?关键在于如何最大限度的减小树的深度。正是基于这个想法,平衡二叉树出现了。
平衡二叉树的定义 (AVL—— 发明者为Adel’son-Vel’skii 和 Landis)
平衡二叉查找树,又称 AVL树。 它除了具备二叉查找树的基本特征之外,还具有一个非常重要的特点:它 的左子树和右子树都是平衡二叉树,且左子树和右子树的深度之差的绝对值(平衡因子 ) 不超过1。 也就是说AVL树每个节点的平衡因子只可能是-1、0和1(左子树高度减去右子树高度)。
那么如何是二叉查找树在添加数据的同时保持平衡呢?基本思想就是:当在二叉排序树中插入一个节点时,首先检查是否因插入而破坏了平衡,若 破坏,则找出其中的最小不平衡二叉树,在保持二叉排序树特性的情况下,调整最小不平衡子树中节点之间的关系,以达 到新的平衡。所谓最小不平衡子树 指离插入节点最近且以平衡因子的绝对值大于1的节点作为根的子树。
平衡二叉树的操作
查找操作
平衡二叉树的查找基本与二叉查找树相同。插入操作
在平衡二叉树中插入结点与二叉查找树最大的不同在于要随时保证插入后整棵二叉树是平衡的。那么调整不平衡树的基本方法就是: 旋转 。 下面我们归纳一下平衡旋转的4种情况:
这里我们把必须重新平衡的节点叫做A。
平衡因子:左子树的深度减去右子树的深度。
相关旋转图示见 严蔚敏《数据结构》(C语言版)P235- 单旋转–向右旋转平衡处理–在A的左儿子的左子树进行一次插入。此时平衡因子变为2。
- 单旋转–向左旋转平衡处理–在A的右儿子的右子树进行一次插入操作。此时平衡因子变为-2.
- 双旋转–先右后左旋转平衡处理–在A的左儿子的右子树进行一次插入操作。此时平衡因子变为2.
- 双旋转–先左后右平衡处理–在A的右儿子的左子树进行一次插入操作。此时平衡因子变为-2.
代码实现:
/**
*AVL 树 插入新节点 的实现
*/
struct AVLNode{
int val;
AVLNode* left;
AVLNode* right;
int height;
AVLNode(const int& value, AVLNode* lt, AVLNode* rt, int h = 0):val(value), left(lt),right(rt),height(h){}
};
/**
*Return thr height of node t ot -1 if NULL
* /
int height(AVLNode* t) const
{
return t == NULL ? -1 : t->height;
}
void insert(const int& x, AVLNode* & t)
{
if(t == NULL) //empty Tree
t = new AVLNode(x, NULL, NULL);
else if(x < t->val) //插入到左儿子
{
//insert into left subtree
insert(x, t->left);
if(height(t->left) - height(t->right) == 2)
{
if(x < t->left->val) //插入到了左儿子的左子树中 ,向右单旋转
rotateWithLeftChild(t);
else //插入到左儿子的右子树, 先左后右双旋转
doubleRotateWithLeftChild(t);
}
}
else if(x > t->val) //插入到右儿子
{
insert(x, t->right);
if(height(t->right) - height(t->left) == 2)
{
if(x > t->right->val)
rotateWithRightChild(t); //插入到右儿子的右子树, 向左单旋转
else if(x < t->right->val)
doubleRotateWithRightChild(t); //插入到右儿子的左子树, 先右后左双旋转
}
}
else {
//待插入节点已经存在, do nothing
}
t->height = max(height(t->left), height(t->right)) + 1;
}
//向右单旋转
void rotateWithLeftChild( AVLNode* & root)
{
AVLNode *newRoot = root->left;
root->left = newRoot->right;
newRoot->right = root;
root->height = max(height(root->left), height(root->right)) + 1;
newRoot->height = max(height(newRoot->left), root->height) + 1;
root = newRoot; //root 指向新的root节点
}
//向左单旋转
void rotateWithRightChild(AVLNode* & root)
{
AVLNode* newRoot = root->right;
root->right = newRoot->left;
newRoot->left = root;
root->height = max(height(root->left), height(root->right)) + 1;
newRoot->height = max(height(newRoot->right), root->height) + 1;
root = newRoot;
}
//先左后右双旋转
void doubleRotateWithLeftChild(AVLNode* & root)
{
rotateWithRightChild(root->left); //先对root的左子树进行向左单旋转
rotateWithLeftChild(root); //然后对root进行向右单旋转
}
//先右后左双旋转
void doubleRotateWithRightChild(AVLNode* & root)
{
rotateWithLeftChild(root->right); //先对root的右子树进行向右单旋转
rotateWithRightChild(root); //然后对root进行向左单旋转
}
平衡二叉树性能分析
平衡二叉树的性能优势:
很显然,平衡二叉树的优势在于不会出现普通二叉查找树的最差情况。其查找的时间复杂度为O(logN)。
在平衡树上进行查找的过程和二叉排序树相同,因此,在查找的过程中和给定值进行比较的关键字个数不超过树的深度。
那么,
含有n个关键字的平衡树的最大深度是多少呢?
为了解答这个问题,
可以借助 深度为h的平衡树所具有的最少节点数的计算公式:
最少节点数
S(h) = S(h - 1) + S(h - 2) + 1
其中,h = 0, S(h) = 1; h = 1, S(h) = 2.
得到h的最大值。
平衡二叉树的缺陷:
(1) 很遗憾的是,为了保证高度平衡,动态插入和删除的代价也随之增加。因此,我们在下一专题中讲讲《红黑树》 这种更加高效的查找结构。
(2) 所有二叉查找树结构的查找代价都与树高是紧密相关的,能否通过减少树高来进一步降低查找代价呢。我们可以通过多路查找树的结构来做到这一点,在后面专题中我们将通过《多路查找树/B-树/B+树 》来介绍。
(3) 在大数据量查找环境下(比如说系统磁盘里的文件目录,数据库中的记录查询 等),所有的二叉查找树结构(BST、AVL、RBT)都不合适。如此大规模的数据量(几G数据),全部组织成平衡二叉树放在内存中是不可能做到的。那么把这棵树放在磁盘中吧。问题就来了:假如构造的平衡二叉树深度有1W层。那么从根节点出发到叶子节点很可能就需要1W次的硬盘IO读写。大家都知道,硬盘的机械部件读写数据的速度远远赶不上纯电子媒体的内存。 查找效率在IO读写过程中将会付出巨大的代价。在大规模数据查询这样一个实际应用背景下,平衡二叉树的效率就很成问题了。对这一问题的解决:我们也会在《多路查找树/B-树/B+树 》 将详细分析。
上面提到的红黑树和多路查找树都是属于深度有界查找树(depth-bounded tree —DBT)
平衡二叉查找树 AVL 的实现的更多相关文章
- 【查找结构3】平衡二叉查找树 [AVL]
在上一个专题中,我们在谈论二叉查找树的效率的时候.不同结构的二叉查找树,查找效率有很大的不同(单支树结构的查找效率退化成了顺序查找).如何解决这个问题呢?关键在于如何最大限度的减小树的深度.正是基于这 ...
- 006-数据结构-树形结构-二叉树、二叉查找树、平衡二叉查找树-AVL树
一.概述 树其实就是不包含回路的连通无向图.树其实是范畴更广的图的特例. 树是一种数据结构,它是由n(n>=1)个有限节点组成一个具有层次关系的集合. 1.1.树的特性: 每个结点有零个或多个子 ...
- 面试题:什么叫平衡二叉查找树--AVL树
查找.插入和删除在平均和最坏情况下都是O(log n) 增加和删除可能需要通过一次或多次树旋转来重新平衡这个树 节点的平衡因子是它的左子树的高度减去它的右子树的高度.带有平衡因子 1.0 或 -1 的 ...
- AVL树(平衡二叉查找树)
首先要说AVL树,我们就必须先说二叉查找树,先介绍二叉查找树的一些特性,然后我们再来说平衡树的一些特性,结合这些特性,然后来介绍AVL树. 一.二叉查找树 1.二叉树查找树的相关特征定义 二叉树查找树 ...
- 平衡树初阶——AVL平衡二叉查找树+三大平衡树(Treap + Splay + SBT)模板【超详解】
平衡树初阶——AVL平衡二叉查找树 一.什么是二叉树 1. 什么是树. 计算机科学里面的树本质是一个树状图.树首先是一个有向无环图,由根节点指向子结点.但是不严格的说,我们也研究无向树.所谓无向树就是 ...
- 数据结构-自平衡二叉查找树(AVL)详解
介绍: 在计算机科学中,AVL树是最先发明的自平衡二叉查找树. 在AVL树中任何节点的两个子树的高度最大差别为一,所以它也被称为高度平衡树. 查找.插入和删除在平均和最坏情况下都是O(log n).增 ...
- 算法学习 - 平衡二叉查找树实现(AVL树)
平衡二叉查找树 平衡二叉查找树是非常早出现的平衡树,由于全部子树的高度差不超过1,所以操作平均为O(logN). 平衡二叉查找树和BS树非常像,插入和删除操作也基本一样.可是每一个节点多了一个高度的信 ...
- 树的平衡之AVL树——错过文末你会后悔,信我
学习数据结构应该是一个循序渐进的过程: 当我们学习数组时,我们要体会数组的优点:仅仅通过下标就可以访问我们要找的元素(便于查找). 此时,我们思考:假如我要在第一个元素前插入一个新元素?采用数组需要挪 ...
- 二叉查找树(二叉排序树)的详细实现,以及随机平衡二叉查找树Treap的分析与应用
这是一篇两年前写的东西,自我感觉还是相当不错的Treap教程.正好期末信息科学技术概论课要求交一个论文,就把这个东西修改了一下交了,顺便也发到这里吧. 随机平衡二叉查找树Treap的分析与应用 1.序 ...
随机推荐
- 添加、修改、删除XML节点代码例子
version="1.0" encoding="gb2312"?> . <bookstore> . <book genre=" ...
- VS2013 自定义项目模板以及制作.vsix文件
一.环境检查 打开VS2013新建项目.如果在"其他项目类型"中不包含扩展性节点,则需要下载并安装vs2013 SDK. 二.创建项目模板 1,在VS中新建一个类库项目(此处仅以类 ...
- carousel 插件隐藏列表中几项导致左右切换出错
1. 一般的应用场景: 用于左右快速切换显示的列表内容,比如对员工的切换. 对于这种情况必不可少需要按照部门进行搜索,目前我的做法是首次加载所有该用户可以查看的员工列表,选择部门后又选择的隐藏掉其他不 ...
- Python函数(2)
一.函数对象 函数是第一类对象:指的是函数名指向的值可以被当作数据去使用. 1.函数可以被引用 例如: 2.可以当作参数传递给另一个函数 例如: 3.可以当作一个函数的返回值 例如: 4.可以当作容器 ...
- 缺陷=bug?
Defect(缺陷):是指静态处在于软件工作产品(文档.代码)中的错误,也指软件运行时由于这些错误被激发导致的软件产品与其属性的偏离现象. Bug:Bug通常是软件缺陷(Defect)导致的一些软件故 ...
- Swagger2:常用注解说明
Swagger2常用注解说明 Spring Boot : Swagger 2使用教程:https://www.cnblogs.com/JealousGirl/p/swagger.html 这里只讲述@ ...
- 利用XShell和WinSCP连接本机和Linux虚拟机——Kali部署
1.XShell支持在本机直接连接Linux终端,加快速度,支持命令的复制粘贴 2.WinSCP 支持本机与Linux的文件复制粘贴 关键:使用SSH协议,所以要在Linux开启ssh服务,下面以Ka ...
- session的MaxInactiveInterval=0在tomcat6和tomcat8不同表现
在tomcat6中调用 request.getSession().setMaxInactiveInterval(0); 这个session会立即过期. 而在tomcat8中,同样的调用,会导致这个se ...
- 【JavaScript 封装库】Prototype 原型版发布!
/* 源码作者: 石不易(Louis Shi) 联系方式: http://www.shibuyi.net =============================================== ...
- VUE的组件DEMO
组件的基本写法可以如下: HTML: <div id="components-demo"> <button-counter self-data="thi ...