DS AVL树详解
先说说二叉搜索树: 是有序的二叉树,根值>左节点值,右节点值>根值。
如果要查找某个值,二叉搜索树和二分查找一样,每进行一次值比较,就会减少一半的遍历区间。
但是,如果树插入的值一直递增/递减,就会出现这种情况:
这样,二叉树性能就完全失去了,直接退化成了顺序表,查找效率低下。
由此,引入了能保持性能最佳的二叉搜索树。
AVL树: 具有高度平衡的二叉搜索树。
性质: 1.它的左右子树都是AVL树
2.左右子树高度差(简称平衡因子)的绝对值不超过1
搜索的时间复杂度: O(log2(n))
AVL树节点
struct AVLTree{
pair<K,V> kv;
AVLTreeNode* left;
AVLTreeNode* right;
AVLTreeNode* parent; int bf; //balance factor
};
与二叉搜索树不同的是引入了父节点(方便后面讲的旋转)和平衡因子(保持高度平衡的核心)。
AVL树的插入:
在插入节点后,需要调整平衡:
1.bf更新规则:新增在左 父亲bf-1 ; 新增在右 父亲bf+1
2.持续往祖先更新 如果 祖先bf==0,祖先肯定是从1或者-1变来的,那就说明祖先所在子树的高度不变,停止更新;
祖先|bf|==1,祖先肯定是从0变来的,说明祖先所在子树高度变了,继续往上更新;
祖先|bf|==2,祖先肯定是从1或者-1变来的,说明高度已经不平衡了,需要旋转调整;
旋转:
1.左单旋: 新结点插在较高右子树右侧 2 1
2.右单旋: 新结点插在较高左子树左侧 -2 -1
3.左右双旋(先左单旋parent,再右单旋g): 新结点插在较高左子树右侧 -2 1 -> -2 -2
4.右左双旋: 新结点插在较高右子树左侧 2 -1 -> 2 2
总结:
//右单旋
void RotateR(Node* parent)
{
Node* subL = parnet->left;
Node* subLR = subL->right; //右旋下去,原根左孩子成为新根
parent->left = subLR;
if(subLR) //LR不为空,才连接其父亲
subLR->parent = parent; //更新新根与原根的关系
subL->right = parent; //记录原根的父
Node* ppNode = parent->parent;
//更新新根与原根的关系
parent->parent = subL; //下面都是因为双向链表带来的问题
//更新原根父与新根的关系
//新根就是根节点
if(ppNode==nullptr)
{
root = subL;
root->parent = nullptr;
}
//新根更新与原根父的关系
else
{
if(ppNode->left==parent)
ppNode->left = subL;
else
ppNode->right = subL; subL->parent = ppNode;
}
//根据上图,更新部分节点平衡因子
parent->bf = subL->bf = 0;
}
//左单旋同理
void RotateL(Node* parent);
//右左双旋
void RotateRL(Node* parent)
{
Node* subR = parent->right;
Node* subRL = subR->left;
int bf = subRL->bf;
//右左双旋
RotateR(parent->right);
RotateL(parent); //更新双旋后的bf
//由于单旋会将bf置0,而双旋有三种情况,需要记录旋转前的新根的bf
//1.新结点插入后,subRL的bf是0 --- 插入的节点就是subRL
if(bf == 0)
{
parent->bf = subRL->bf = subR->bf = 0;
}
//2.新结点插入后,subRL的bf是1 --- 插入的节点在subRL右边
else if(bf == 1)
{
subR->bf = 0;
parent->bf = -1;
subRL->bf = 0;
}
//3.新结点插入后,subRL的bf是-1 --- 插入的节点在它subRL左边
else if(bf == -1)
{
parent->bf = 0;
subR->bf = 1;
subRL->bf = 0;
}
}
//左右双旋同理
void RotateLR(Node* parnet);
AVL树插入代码
bool Insert(const pair<K,V>& kv)
{
//插入结点
if(root == nullptr)
{
root=new Node(kv);
root->bf = 0;
return true;
}
Node* parent = nullptr;
Node* cur = root;
while(cur)
{
if(cur->kv.first < kv.first) else if(cur->kv.first > kv.first) else
return false;
}
cur = new Node(kv);
//父节点连接插入的结点
if(parent->kv.first < kv.first)
{
parent->right = cur;
cur->parent = parent;
}
else
{
...
} //调平衡
//1.新增在左 父亲bf-1 新增在右 父亲bf+1
//2.持续往祖先更新 if 祖先bf == 0,则祖先所在子树高度不变,停止往上更新
// if 祖先|bf| == 1,则祖先所在子树高度变了,继续往上更新
// if 祖先|bf| == 2,则祖先所在树不平衡,则旋转调整
//1.更新平衡因子
while(parent)
{
if(cur == parent->right)
parent->bf++;
else
parent->bf--; //高度不变,更新完成
if(parent->bf == 0)
break;
//高度变了,继续更新
else if(abs(parent->bf) == 1)
{
cur = parent;
parent = parent->parent;
}
//不平衡,旋转调整
else if(abs(parent->bf) == 2)
{
//判断旋转方式
if(parent->bf==2)
{
if(cur->bf==1)
RotateL(parent);
else if(cur->bf==-1)
RotateRL(parent);
}
else if(parent->bf==-2)
{
if(cur->bf==-1)
RotateR(parent);
else if(cur->bf==1)
RotateLR(parent);
}
break;
}
else
assert(false);
}
}
因为引入了bf和双向链表,所以有了很多坑,应该避免以下两点:
1.单旋完成后,注意更新其新根与原根父节点的连接关系
2.双旋完成后,注意根据双旋规律进行bf更新
DS AVL树详解的更多相关文章
- 数据结构图文解析之:AVL树详解及C++模板实现
0. 数据结构图文解析系列 数据结构系列文章 数据结构图文解析之:数组.单链表.双链表介绍及C++模板实现 数据结构图文解析之:栈的简介及C++模板实现 数据结构图文解析之:队列详解与C++模板实现 ...
- AVL树详解
AVL树 参考了:http://www.cppblog.com/cxiaojia/archive/2012/08/20/187776.html 修改了其中的错误,代码实现并亲自验证过. 平衡二叉树(B ...
- trie字典树详解及应用
原文链接 http://www.cnblogs.com/freewater/archive/2012/09/11/2680480.html Trie树详解及其应用 一.知识简介 ...
- Linux DTS(Device Tree Source)设备树详解之二(dts匹配及发挥作用的流程篇)【转】
转自:https://blog.csdn.net/radianceblau/article/details/74722395 版权声明:本文为博主原创文章,未经博主允许不得转载.如本文对您有帮助,欢迎 ...
- JavaScript---Dom树详解,节点查找方式(直接(id,class,tag),间接(父子,兄弟)),节点操作(增删改查,赋值节点,替换节点,),节点属性操作(增删改查),节点文本的操作(增删改查),事件
JavaScript---Dom树详解,节点查找方式(直接(id,class,tag),间接(父子,兄弟)),节点操作(增删改查,赋值节点,替换节点,),节点属性操作(增删改查),节点文本的操作(增删 ...
- 线段树详解 (原理,实现与应用)(转载自:http://blog.csdn.net/zearot/article/details/48299459)
原文地址:http://blog.csdn.net/zearot/article/details/48299459(如有侵权,请联系博主,立即删除.) 线段树详解 By 岩之痕 目录: 一:综述 ...
- Linux dts 设备树详解(二) 动手编写设备树dts
Linux dts 设备树详解(一) 基础知识 Linux dts 设备树详解(二) 动手编写设备树dts 文章目录 前言 硬件结构 设备树dts文件 前言 在简单了解概念之后,我们可以开始尝试写一个 ...
- Linux dts 设备树详解(一) 基础知识
Linux dts 设备树详解(一) 基础知识 Linux dts 设备树详解(二) 动手编写设备树dts 文章目录 1 前言 2 概念 2.1 什么是设备树 dts(device tree)? 2. ...
- trie树--详解
文章作者:yx_th000 文章来源:Cherish_yimi (http://www.cnblogs.com/cherish_yimi/) 转载请注明,谢谢合作.关键词:trie trie树 数据结 ...
随机推荐
- Anniversary party(hdu1520)(poj2342)题解
原题地址:http://poj.org/problem?id=2342 题目大意: 上司和下属不能同时参加派对,求参加派对的最大活跃值. 关系满足一棵树,每个人都有自己的活跃值(-128~127) 求 ...
- 转载:关于思科交换机、路由器如何关闭telnet 开启ssh服务
等保测评要求: 必须关闭telnet服务,开启ssh服务 即用ssh方式登录网络设备,而不允许用telnet. 输入密码.en 再次输入密码.sh run 这些常规动作就不再赘述. 1.关闭telne ...
- MyBatis传入参数为list、数组、map写法
1.foreach简单介绍: foreach的主要用在构建in条件中,它可以在SQL语句中进行迭代一个集合. foreach元素的属性主要有item,index,collection,open,sep ...
- fastjson在反序列化时,解析对象中的继承,抽象类处理
LimitActionConfig是ActionConfig的子类,RuleConfig的有个属性是ActionConfig,需要反序列化成LimitActionConfig ParserConfig ...
- arthas安装进docker
教程参照https://alibaba.github.io/arthas/arthas-tutorials mkdir /opt/downloads -pmkdir /opt/arthas -p下载地 ...
- Error-ASP.NET:未能加载文件或程序集“CMSCalendar”或它的某一个依赖项。系统找不到指定的文件。
ylbtech-Error-ASP.NET:未能加载文件或程序集“CMSCalendar”或它的某一个依赖项.系统找不到指定的文件. 1.返回顶部 1. “/”应用程序中的服务器错误. 分析器错误 说 ...
- 前端解析 excel docx
在研究中... https://www.npmjs.com/package/xlsx https://www.jianshu.com/p/68a420a68ded https://www.jiansh ...
- 修改Linux系统时间EDT改为CST
今天发现一台服务器时间比北京时间慢 12 个小时,使用 date 命令后发现是: 2019年 06月 04日 星期二 21:50:33 EDT EDT 时间即美国东部时间.这里要改为北京时间即可: m ...
- RabbitMQ安装后无法访问15672端口
切换到RabbitMQ的安装目录 sbin 目录下执行: rabbitmq-plugins enable rabbitmq_management 即可打开管理界面. rabbitmq的web管理界面无 ...
- LeetCode 108. Convert Sorted Array to Binary Search Tree (将有序数组转换成BST)
108. Convert Sorted Array to Binary Search Tree Given an array where elements are sorted in ascendin ...