1.概念

平衡二叉树(AVL Tree)首先要满足二叉树的定义,如下

  • 二叉排序树或者是一棵空树,或者是具有下列性质的二叉树:
  • 若左子树不空,则左子树上所有结点的值均小于它的根结点的值;
  • 若右子树不空,则右子树上所有结点的值均大于它的根结点的值;
  • 左、右子树也分别为二叉排序树;
  • 没有键值相等的节点。
  • 平衡度是左子树高度减去右子树高度,平衡度只能是-1,+1,0
下图给出了一个非平衡二叉排序树和平衡二叉排序树
说明:结点上方的数字是平衡度(平衡因子),a图9结点左子树比右子树高2,不满足AVL Tree的定义,所以是非平衡树
b图所有结点度数绝对值没有超过1,所以满足平衡树的定义,是平衡树
  • 危机结点是指平衡度为1,有可能破坏平衡树的结点
  • 左改组是指新结点插入到危机结点的左树下
  • 右改组是指新新结点插入到危机结点的右子树下
  • LL是新结点插入在危机节点的左子树的左子树上
  • LR是新结点插入在危机节点的左子树的右子树上
  • RR是新结点插入在危机节点的右子树的右子树上
  • RL是新结点插入在危机节点的右子树的左子树上
 

2.代码实现

当前树的结构

11
          /   \
        7       15
       / \    /  \
      3      9  14      18
     / \     /    / \
    1   5    12   16     20
                     /
                   26

2.1 定义平衡树结点:

平衡因子可以是右子树高度减去左子树高度,不同的教材定义不一样,我这里按照左树-右树来做

template<class K, class V>
struct AVLTreeNode
{
K _key; //树权值
V _value;
int _bf; //平衡因子 -1,0,1 (每个节点的平衡因子等于左子树的高度减去右子树的高度)
//有的教材定义平衡度是左子树高度减去右子树,都是可以的
AVLTreeNode<K, V>* _parent; //指向父节点的指针
AVLTreeNode<K, V>* _left; //指向左孩子的指针
AVLTreeNode<K, V>* _right; //指向右孩子的指针 AVLTreeNode(const K& key = K(), const V& value = V())
:_key(key)
, _value(value)
, _bf()
, _parent(NULL)
, _left(NULL)
, _right(NULL)
{}
};

2.2 左改组图解

左右改组是为了方便我们插入删除的时候保持二叉树平衡而引入的概念

左改组LL型和LR(a),LR(b),LR(c)型图解

2.3 左改组LL型

首先声明一个构造的左子树,subL其实就是危机结点,subLR是危机节点的右子树,ppNode是祖先节点

构建parent子树,将parent和subL连接起来

如果祖先结点为空,将当前结点subL置为根节点,请参见上图(a‘)的情况,B是危机结点,调整过后变成了根节点

否则祖父结点赋给subL的父结点,判断父节点是否是祖先结点的左子树,是的话,用构造的左子树替代之

不是就用subL替代祖先节点的右子树

//左改组LL型
template<class K, class V>
void AVLTree<K, V>::_RotateLL(AVLTreeNode<K, V>*& parent)
{
AVLTreeNode<K, V>* subL = parent->_left; //构造的左子树
AVLTreeNode<K, V>* subLR = subL->_right;//subL的右子树
AVLTreeNode<K, V>* ppNode = parent->_parent;//标记祖先节点
//1.构建parent子树 将parent和subLR链接起来
parent->_left = subLR;
if (subLR) subLR->_parent = parent;
//2.构建subL子树 将subL与parent链接起来
subL->_right = parent;
parent->_parent = subL;
//3.将祖先节点与subL链接起来
if (ppNode == NULL)
{ //如果祖先为NULL,说明当前subL节点为根节点
subL->_parent = NULL;
_root = subL;
}
else
{
subL->_parent = ppNode;
if (ppNode->_left == parent)
ppNode->_left = subL;
else if (ppNode->_right == parent)
ppNode->_right = subL;
}
//4.重置平衡因子
parent->_bf = ;
subL->_bf = ;
//5.更新subL为当前父节点
parent = subL;
}

2.4 左改组LR(a)、LR(b)和LR(c)型

pNode是当前父节点,subR是构造的右子树,subLR是subR的左子树

对当前父节点LL左改组,再右改组

根据平衡因子判断是LR什么类型,请参见上图图解(b),(c),(d)的情况

//左改组LR型
template<class K, class V>
void AVLTree<K, V>::_RotateLR(AVLTreeNode<K, V>*& parent)
{
AVLTreeNode<K, V>* pNode = parent;
AVLTreeNode<K, V>* subR = parent->_right;
AVLTreeNode<K, V>* subLR = subR->_left;
int bf = subLR->_bf; _RotateLL(parent->_right);
_RotateRR(parent);
//LR(b)型
if (bf == -)
{
pNode->_bf = ;
subR->_bf = ;
}
//LR(a)型
else if (bf == )
{
pNode->_bf = -;
subR->_bf = ;
}
//LR(c)型
else
{
pNode->_bf = ;
subR->_bf = ;
}
}

右改组和左改组镜像对称,反过来就行了

2.5 插入函数

AVL树是空,将当前结点直接置为根节点

AVL树在满足平衡度的要求下,和二叉排序树一致,key小于当前结点,转到当前结点左子树,key大于当前结点,转到当前结点右子树

将parent的左子树赋予当前结点,更新平衡因子,_bf++

将parent的右子树赋予当前结点,更新平衡因子,_bf--

如果合法,即平衡因子=0,终止当前循环

如果当前结点是危机结点,即平衡度绝对值等于1,当前结点往上回溯,变成父节点,继续检查它的平衡度

接下来是平衡异常的情况,父结点平衡度为2,当前结点(危机结点)平衡度为1,进入左改组LL,LL介绍参考2.2 左改组LL

当前结点平衡度为-1,进入左改组LR,LR介绍参考2.3 左改组LR

右改组的情况类似

template<class K, class V>
bool AVLTree<K, V>::Insert(const K& key, const V& value)
{
//1.空树
if (_root == NULL)
{
_root = new AVLTreeNode<K, V>(key, value);
return true;
} //2.AVL树不为NULL
AVLTreeNode<K, V>* parent = NULL;
AVLTreeNode<K, V>* cur = _root;
//找到数据插入位置
while (cur)
{
if (cur->_key < key)
{
parent = cur;
cur = cur->_right;
}
else if (cur->_key > key)
{
parent = cur;
cur = cur->_left;
}
else
{
return false;
}
}
//插入数据
cur = new AVLTreeNode<K, V>(key, value);
cur->_parent = parent;
if (parent->_key > key)
parent->_left = cur;
else
parent->_right = cur; while (parent)
{
//更新平衡因子
if (cur == parent->_left)
parent->_bf++;
else if (cur == parent->_right)
parent->_bf--; //检验平衡因子是否合法
if (parent->_bf == )
break;
else if (parent->_bf == - || parent->_bf == )
{ // 回溯上升 更新祖父节点的平衡因子并检验合法性
cur = parent;
parent = cur->_parent;
}
// 2 -2 平衡因子不合法 需要进行旋转 降低高度
else
{
if (parent->_bf == -)
{
if (cur->_bf == -)
_RotateRR(parent);
else
_RotateLR(parent);
}
else if (parent->_bf == )
{
if (cur->_bf == )
_RotateLL(parent);
else
_RotateRL(parent);
}
break;
}
}
}

2.6 遍历方法

//中序遍历
template<class K, class V>
void AVLTree<K, V>::_InOrder(AVLTreeNode<K, V>* root)
{
if (root == NULL)
return;
_InOrder(root->_left);
cout << root->_key << " ";
_InOrder(root->_right);
}
//前序遍历
template<class K, class V>
void AVLTree<K, V>::_PreOrder(AVLTreeNode<K, V>* root)
{
if (root == NULL)
return;
cout << root->_key << " ";
_PreOrder(root->_left);
_PreOrder(root->_right);
}
//后序遍历
template<class K, class V>
void AVLTree<K, V>::_PostOrder(AVLTreeNode<K, V>* root)
{
if (root == NULL)
return;
_PostOrder(root->_left);
_PostOrder(root->_right);
cout << root->_key << " ";
}

3.运行和源码

运行效果如下

源代码:https://github.com/cjy513203427/C_Program_Base/tree/master/61.%E5%B9%B3%E8%A1%A1%E4%BA%8C%E5%8F%89%E6%A0%91/61.%E5%B9%B3%E8%A1%A1%E4%BA%8C%E5%8F%89%E6%A0%91

C++实现平衡二叉树的更多相关文章

  1. 算法与数据结构(十一) 平衡二叉树(AVL树)

    今天的博客是在上一篇博客的基础上进行的延伸.上一篇博客我们主要聊了二叉排序树,详情请戳<二叉排序树的查找.插入与删除>.本篇博客我们就在二叉排序树的基础上来聊聊平衡二叉树,也叫AVL树,A ...

  2. [LeetCode] Balanced Binary Tree 平衡二叉树

    Given a binary tree, determine if it is height-balanced. For this problem, a height-balanced binary ...

  3. Java数据结构——平衡二叉树的平衡因子(转自牛客网)

    若向平衡二叉树中插入一个新结点后破坏了平衡二叉树的平衡性.首先要找出插入新结点后失去平衡的最小子树根结点的指针.然后再调整这个子树中有关结点之间的链接关系,使之成为新的平衡子树.当失去平衡的最小子树被 ...

  4. 【数据结构】平衡二叉树—AVL树

    (百度百科)在计算机科学中,AVL树是最先发明的自平衡二叉查找树.在AVL树中任何节点的两个子树的高度最大差别为一,所以它也被称为高度平衡树.查找.插入和删除在平均和最坏情况下都是O(log n).增 ...

  5. 平衡二叉树AVL删除

    平衡二叉树的插入过程:http://www.cnblogs.com/hujunzheng/p/4665451.html 对于二叉平衡树的删除采用的是二叉排序树删除的思路: 假设被删结点是*p,其双亲是 ...

  6. 平衡二叉树AVL插入

    平衡二叉树(Balancedbinary tree)是由阿德尔森-维尔斯和兰迪斯(Adelson-Velskiiand Landis)于1962年首先提出的,所以又称为AVL树. 定义:平衡二叉树或为 ...

  7. 数据结构快速回顾——平衡二叉树 AVL (转)

    平衡二叉树(Balanced Binary Tree)是二叉查找树的一个进化体,也是第一个引入平衡概念的二叉树.1962年,G.M. Adelson-Velsky 和 E.M. Landis发明了这棵 ...

  8. LeetCode——Balanced Binary Tree(判断是否平衡二叉树)

    问题: Given a binary tree, determine if it is height-balanced. For this problem, a height-balanced bin ...

  9. 数据结构之平衡二叉树(AVL树)

    平衡二叉树(AVL树)定义如下:平衡二叉树或者是一棵空树,或者是具有以下性质的二叉排序树: (1)它的左子树和右子树的高度之差绝对值不超过1: (2)它的左子树和右子树都是平衡二叉树. AVL树避免了 ...

  10. 平衡二叉树AVL

    1.定义 平衡二叉树(Balanced Binary Tree)是二叉查找树的一个改进,也是第一个引入平衡概念的二叉树.1962年,G.M. Adelson-Velsky 和 E.M. Landis发 ...

随机推荐

  1. http方式传递参数值转义或乱码的处理(base64)

    如果通过http方式传递参数url编码了,可用urlEncode和urlDecode,这种方式不同开发语言编码出来的可能不同,所以不同开发语言最好用base64编码和解码来处理: base64加密: ...

  2. [Openwrt扩展中篇]添加Aria2和webui

    上一篇说了我构建了简单的网络硬盘,这一篇说的是我构造的aria2和webui,大概是这样我觉得有了网络硬盘,那么我是不是可以远程下载呢,翻阅了网上资料发现迅雷的Xware貌似不更新了,然后我发现了ar ...

  3. 使用纯注解与配置类开发springMVC项目,去掉xml配置

    最近拜读了杨开振老师的书,深入浅出springBoot2.x,挖掘了很多以前被忽略的知识, 开发一年多,工作中一直用传统springmvc的开发,基本都还是用的传统的xml配置开发, 看到书里有提到, ...

  4. 在c++中实现反射的初步想法

    最近在思考如何在c++中实现反射.事情的起因是这样的:我们服务器是用c++开发的,如果需要写一些测试用的GM指令的话,需要编写完GM代码后重新编译并且重启进程,工序繁琐且比较耗时.因此就有了想用脚本( ...

  5. Ubuntu server 搭建Git server【转】

    转自:http://www.cnblogs.com/candle806/p/4064610.html Ubuntu server 搭建Git server,git相比svn,最主要就是分布式了,每个客 ...

  6. linux音频alsa-uda134x驱动分析之二(时钟)

    Audio Clocking音频时钟============== This text describes the audio clocking terms in ASoC and digital au ...

  7. linux下C获取系统时间的方法

    asctime(将时间和日期以字符串格式表示)  相关函数 time,ctime,gmtime,localtime  表头文件 #include  定义函数 char * asctime(const ...

  8. 金蝶K3WISE常用数据表

    K3Wise 14.2 清空密码update t_User set FSID=') F ", ,P T #8 *P!D &D 80!N &@ <0 C '+''''+' ...

  9. Mac 升级一次,php 就崩溃一次,有味,苹果....

    Mac升级系统macOS Sierra后PHP不编译 Mac下搭建PHP开发环境(Apache+PHP+MySQL+phpMyAdmin),当Mac 从OS 10.11升级至macOS Sierra( ...

  10. poj3636

    题意:每个物品有两个属性:长和宽(长宽不可互换).如果一个物品的长和宽均大于另一个物品,则这个物品可以罩住另一个物品,用这种罩住物品的方法将物品分组,一组之内的物品可以一个罩住一个的全部罩起来.问最少 ...