先说说二叉搜索树: 是有序的二叉树,根值>左节点值,右节点值>根值。

    如果要查找某个值,二叉搜索树和二分查找一样,每进行一次值比较,就会减少一半的遍历区间。

    但是,如果树插入的值一直递增/递减,就会出现这种情况:

    这样,二叉树性能就完全失去了,直接退化成了顺序表,查找效率低下。

    由此,引入了能保持性能最佳的二叉搜索树。

    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

    总结:

        1. pParent的平衡因子为2,说明pParent的右子树高,设pParent的右子树的根为pSubR
      当pSubR的平衡因子为1时,执行左单旋
      当pSubR的平衡因子为-1时,执行右左双旋
      2. pParent的平衡因子为-2,说明pParent的左子树高,设pParent的左子树的根为pSubL
      当pSubL的平衡因子为-1是,执行右单旋
      当pSubL的平衡因子为1时,执行左右双旋
      旋转完成后,原pParent为根的子树个高度降低,已经平衡,不需要再向上更新。
 
      AVL树旋转代码:
//右单旋
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树详解的更多相关文章

  1. 数据结构图文解析之:AVL树详解及C++模板实现

    0. 数据结构图文解析系列 数据结构系列文章 数据结构图文解析之:数组.单链表.双链表介绍及C++模板实现 数据结构图文解析之:栈的简介及C++模板实现 数据结构图文解析之:队列详解与C++模板实现 ...

  2. AVL树详解

    AVL树 参考了:http://www.cppblog.com/cxiaojia/archive/2012/08/20/187776.html 修改了其中的错误,代码实现并亲自验证过. 平衡二叉树(B ...

  3. trie字典树详解及应用

    原文链接    http://www.cnblogs.com/freewater/archive/2012/09/11/2680480.html Trie树详解及其应用   一.知识简介        ...

  4. Linux DTS(Device Tree Source)设备树详解之二(dts匹配及发挥作用的流程篇)【转】

    转自:https://blog.csdn.net/radianceblau/article/details/74722395 版权声明:本文为博主原创文章,未经博主允许不得转载.如本文对您有帮助,欢迎 ...

  5. JavaScript---Dom树详解,节点查找方式(直接(id,class,tag),间接(父子,兄弟)),节点操作(增删改查,赋值节点,替换节点,),节点属性操作(增删改查),节点文本的操作(增删改查),事件

    JavaScript---Dom树详解,节点查找方式(直接(id,class,tag),间接(父子,兄弟)),节点操作(增删改查,赋值节点,替换节点,),节点属性操作(增删改查),节点文本的操作(增删 ...

  6. 线段树详解 (原理,实现与应用)(转载自:http://blog.csdn.net/zearot/article/details/48299459)

    原文地址:http://blog.csdn.net/zearot/article/details/48299459(如有侵权,请联系博主,立即删除.) 线段树详解    By 岩之痕 目录: 一:综述 ...

  7. Linux dts 设备树详解(二) 动手编写设备树dts

    Linux dts 设备树详解(一) 基础知识 Linux dts 设备树详解(二) 动手编写设备树dts 文章目录 前言 硬件结构 设备树dts文件 前言 在简单了解概念之后,我们可以开始尝试写一个 ...

  8. Linux dts 设备树详解(一) 基础知识

    Linux dts 设备树详解(一) 基础知识 Linux dts 设备树详解(二) 动手编写设备树dts 文章目录 1 前言 2 概念 2.1 什么是设备树 dts(device tree)? 2. ...

  9. trie树--详解

    文章作者:yx_th000 文章来源:Cherish_yimi (http://www.cnblogs.com/cherish_yimi/) 转载请注明,谢谢合作.关键词:trie trie树 数据结 ...

随机推荐

  1. 《ELK Stack权威指南》读书笔记

    Logstack: 1.Logstack介绍:Logstash is an open source data collection engine with real-time pipelining c ...

  2. xss、SQL测试用例小结

    xss测试用例小结: <script>alert("跨站")</script> (最常用)<img scr=javascript:alert(&quo ...

  3. pycharm配置默认代码和注释模板

    有人问,在pycharm新建python文件时,文件开头的注释每次都要重复写,能不能配置成模板?其实pycharm本身自带此功能 在pycharm菜单栏找File -> settings -&g ...

  4. .NET Core 代码安装服务启动

    最近做了一些.NET Core的程序,有在Windows下运行的 有在CentOS 下运行的,Windows下运行的还好,对Windows下还算比较熟悉了,但CentOS 下 每次都是找笔记支持命令 ...

  5. GlusterFS 安装

    一.简介 GlusterFS 是近年兴起的一个高性能开源分布式文件系统,其目标是全局命名空间.分布式前端的高性能文件系统,目前已被 RedHat 看中,GlusterFS 具有高扩展.高可性.高性能. ...

  6. Leetcode:2. 两数相加

    题目描述: 给出两个 非空 的链表用来表示两个非负的整数.其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字. 如果,我们将这两个数相加起来,则会返回一个新的链表来 ...

  7. abp中使用同步方法调用异步方法

    var result= AsyncHelper.RunSync(()=>{   return  _service.GetUserAsync();   });

  8. NioEventLoopGroup源码分析与线程设定

    我的以Netty Socket编程的代码为例, 1.EventLoopGroup 进入EventLoopGroup,这是一个特殊的EventExecutorGroup,在事件循环中,在selectio ...

  9. wikiquote

    發現了一個很好玩的網站wikiquote,上面有很多引用的句子 比如關於編程語言的說法 https://en.m.wikiquote.org/wiki/Category:Programming_lan ...

  10. eclipse中自定义注释模板

    eclipse中自定义注释模板 2018年10月09日 10:51:27 lm_y 阅读数 857更多 分类专栏: java Java   编辑注释模板的方法:Window->Preferenc ...