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

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

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

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

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

    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. type of的返回值有哪些

    typeof 10; // number typeof 'time'; //string typeof undefined; // undefined typeof null; // object t ...

  2. 全部文章> Maven

    Maven     原 Maven中<resources>标签详解 &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;& ...

  3. SDOI 二轮垫底鸡

    SDOI 二轮垫底鸡 day0 准备爆零 没啥好准备考试的,12.00出发,试机敲抄个ntt,在宾馆不知道颓啥. day1 爆零爬山 T1noip的题目也放到省选上. 第一档线段树?肯定不写,直接上1 ...

  4. ServerManager.exe 0xc0000135 应用程序错误

    问题: 将 Windows Server 2016 .NET Framework移除. IIS卸载后, Server Manager.exe.事件查看器等都无法正常开启. 解决方案: 在运行中,输入C ...

  5. Tecplot——为动画添加求解时间(翻译)

    翻译自:<Tecplot 360 Getting Started Manual>中的Scenic Detour: Add Solution Time Caption 首先展示效果: 在画图 ...

  6. Hyper-V中安装CentOS7设置静态ip并且可以连接外网

    https://blog.csdn.net/xj19940904/article/details/89165002 https://blog.csdn.net/u011598235/article/d ...

  7. C++11原子操作与无锁编程(转)

    不讲语言特性,只从工程角度出发,个人觉得C++标准委员会在C++11中对多线程库的引入是有史以来做得最人道的一件事:今天我将就C++11多线程中的atomic原子操作展开讨论:比较互斥锁,自旋锁(sp ...

  8. 修改Jupyter Notebook默认文件存储路径(已安装Anaconda)

    https://blog.csdn.net/weixin_44799144/article/details/91823079 修改Jupyter Notebook默认文件存储路径首先,安装好Anaco ...

  9. Sublime Text 3.2.1详细安装破解教程,附最新激活码license(全网独家可用有效)

    title: "Sublime Text 3.2.1详细安装破解教程,附最新激活码license(全网独家可用有效)" categories: soft tags: soft au ...

  10. RequireJS - 快速指南

    原文: https://www.tutorialspoint.com/requirejs/requirejs_quick_guide.htm RequireJS - 概述 RequireJS是一个Ja ...