C++实现平衡二叉树
1.概念
平衡二叉树(AVL Tree)首先要满足二叉树的定义,如下
- 二叉排序树或者是一棵空树,或者是具有下列性质的二叉树:
- 若左子树不空,则左子树上所有结点的值均小于它的根结点的值;
- 若右子树不空,则右子树上所有结点的值均大于它的根结点的值;
- 左、右子树也分别为二叉排序树;
- 没有键值相等的节点。
- 平衡度是左子树高度减去右子树高度,平衡度只能是-1,+1,0


- 危机结点是指平衡度为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.运行和源码
运行效果如下

C++实现平衡二叉树的更多相关文章
- 算法与数据结构(十一) 平衡二叉树(AVL树)
今天的博客是在上一篇博客的基础上进行的延伸.上一篇博客我们主要聊了二叉排序树,详情请戳<二叉排序树的查找.插入与删除>.本篇博客我们就在二叉排序树的基础上来聊聊平衡二叉树,也叫AVL树,A ...
- [LeetCode] Balanced Binary Tree 平衡二叉树
Given a binary tree, determine if it is height-balanced. For this problem, a height-balanced binary ...
- Java数据结构——平衡二叉树的平衡因子(转自牛客网)
若向平衡二叉树中插入一个新结点后破坏了平衡二叉树的平衡性.首先要找出插入新结点后失去平衡的最小子树根结点的指针.然后再调整这个子树中有关结点之间的链接关系,使之成为新的平衡子树.当失去平衡的最小子树被 ...
- 【数据结构】平衡二叉树—AVL树
(百度百科)在计算机科学中,AVL树是最先发明的自平衡二叉查找树.在AVL树中任何节点的两个子树的高度最大差别为一,所以它也被称为高度平衡树.查找.插入和删除在平均和最坏情况下都是O(log n).增 ...
- 平衡二叉树AVL删除
平衡二叉树的插入过程:http://www.cnblogs.com/hujunzheng/p/4665451.html 对于二叉平衡树的删除采用的是二叉排序树删除的思路: 假设被删结点是*p,其双亲是 ...
- 平衡二叉树AVL插入
平衡二叉树(Balancedbinary tree)是由阿德尔森-维尔斯和兰迪斯(Adelson-Velskiiand Landis)于1962年首先提出的,所以又称为AVL树. 定义:平衡二叉树或为 ...
- 数据结构快速回顾——平衡二叉树 AVL (转)
平衡二叉树(Balanced Binary Tree)是二叉查找树的一个进化体,也是第一个引入平衡概念的二叉树.1962年,G.M. Adelson-Velsky 和 E.M. Landis发明了这棵 ...
- LeetCode——Balanced Binary Tree(判断是否平衡二叉树)
问题: Given a binary tree, determine if it is height-balanced. For this problem, a height-balanced bin ...
- 数据结构之平衡二叉树(AVL树)
平衡二叉树(AVL树)定义如下:平衡二叉树或者是一棵空树,或者是具有以下性质的二叉排序树: (1)它的左子树和右子树的高度之差绝对值不超过1: (2)它的左子树和右子树都是平衡二叉树. AVL树避免了 ...
- 平衡二叉树AVL
1.定义 平衡二叉树(Balanced Binary Tree)是二叉查找树的一个改进,也是第一个引入平衡概念的二叉树.1962年,G.M. Adelson-Velsky 和 E.M. Landis发 ...
随机推荐
- 双11怎么那么强!之二:浅析淘宝网络通信库tbnet的实现
最近开始看Tair的源码实现,Tair的通信使用的是淘宝的开源的网络库tbnet实现.具体来说是依靠tbnet::Transport类型实现,其源代码路径如下:http://code.taobao.o ...
- Cloudera Manager Admin控制台启动不起来
这几天都在搞大数据这一块,由于以前自己在弄hadoop等安装的时候特别的费劲,于是乎找到了广大程序员的福音——cloudera manager,但是第一步安装好了以后无法启动,再三思考+百度发现: 通 ...
- python初步学习-练习题
1.实现1-100的所有的和 #!/usr/bin/env python #encoding:utf8 '''实现1-100的所有的和 1. 使用列表解析获取0-100的列表 2. 使用reduce内 ...
- Python中的and和or
引子: 出现以上情况的原因是什么呢? print(bool('')) # False print(bool(0)) # False 所有变量的位操作都是通过强制转换成bool实现的,并且表达式的值是从 ...
- Mac 下 gzip 一个文件
gzip -k xxx.json -k 会保留源文件
- Java与JS生成二维码
1.二维码概念 二维码/二维条码是用某种特定的集合图形按一定规律在平面上(二维方向上)分布的黑白相间的图形记录数据符号信息的图片. 黑线是二进制的1,空白的地方是二进制的0,通过1.0这种数据组合用于 ...
- [Ubuntu 14.04] 安装Flash && 安装QQ
一.安装Flash 打开Firefox浏览器弹出的Flash安装提醒早都烦死了,那么Ubuntu14.04怎么安装Flash呢? 1.32位系统命令行安装: 第一步 更新库: sudo apt-get ...
- CentOS7.3下的一个iptables配置
centos7.3默认使用的防火墙应该是firewall,而不是iptables.而我们xxmj服务器使用的是iptables防火墙.所以,在配置防火墙之前,我们需要先关闭firewall,安装ipt ...
- 管道和xargs区别
一直弄不懂,管道不就是把前一个命令的结果作为参数给下一个命令吗,那在 | 后面加不加xargs有什么区别 NewUserFF 写道: 懒蜗牛Gentoo 写道: 管道是实现“将前面的标准输出作为后面的 ...
- Python之容器类Collections
容器类Collections 标签(空格分隔): Python进阶 defaultdict counter deque namedtuple defaultdict defaultdict的作用是可以 ...