[转]C++实现平衡二叉树
作者:Rest探路者
出处:http://www.cnblogs.com/Java-Starter/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意请保留此段声明,请在文章页面明显位置给出原文连接
Github:https://github.com/cjy513203427
目录
正文
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(0)
, _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 = 0;
subL->_bf = 0;
//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 == -1)
{
pNode->_bf = 0;
subR->_bf = 1;
}
//LR(a)型
else if (bf == 1)
{
pNode->_bf = -1;
subR->_bf = 0;
}
//LR(c)型
else
{
pNode->_bf = 0;
subR->_bf = 0;
}
}

右改组和左改组镜像对称,反过来就行了
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 == 0)
break;
else if (parent->_bf == -1 || parent->_bf == 1)
{ // 回溯上升 更新祖父节点的平衡因子并检验合法性
cur = parent;
parent = cur->_parent;
}
// 2 -2 平衡因子不合法 需要进行旋转 降低高度
else
{
if (parent->_bf == -2)
{
if (cur->_bf == -1)
_RotateRR(parent);
else
_RotateLR(parent);
}
else if (parent->_bf == 2)
{
if (cur->_bf == 1)
_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.运行和源码
运行效果如下

作者:Rest探路者
出处:http://www.cnblogs.com/Java-Starter/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意请保留此段声明,请在文章页面明显位置给出原文连接
Github:https://github.com/cjy513203427
[转]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发 ...
随机推荐
- 常用日期计算SQL语句
-- 本月的第一天 SELECT DATEADD(mm, DATEDIFF(mm,0,getdate()), 0) -- 本月的最后一天 SELECT DATEADD(ms,-3,DATEADD(mm ...
- Go_21: Golang 中 time 包的使用二
常量声明: const TimeLayout = "2006-01-02 15:04:05" 这是个奇葩,必须是这个时间点,据说是 go 诞生之日, 记忆方法:6-1-2-3-4- ...
- null和System.DBNull.Value的区别
我记得之前在写一个程序的时候用到了这个知识点,当时判断的时候,有时候null可以,有时候必须是System.DBNull.Value 由于不清楚这两个的区别所以纠结了很久.查了一下,二者的区别如下: ...
- angularJs的继承
为什么要继承,本来是后端的概念,但是同样适用于前端开发.继承,无疑是将通用的东西抽取出来. 下面介绍的是angular的伪继承,就是说是通过继承scope这个变量来实现的.代码很简单,一行代码就可以. ...
- Codeforces #55D-Beautiful numbers (数位dp)
D. Beautiful numbers time limit per test 4 seconds memory limit per test 256 megabytes input standar ...
- Codeforces 314 E. Sereja and Squares
http://codeforces.com/contest/314/problem/E 题意: 原本有一个合法的括号序列 擦去了所有的右括号和部分左括号 问有多少种填括号的方式使他仍然是合法的括号序列 ...
- CSS3 Day1 练习
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- [转]CSS浏览器兼容问题总结
E6.0,ie7.0与Firefox的CSS兼容性问题1.DOCTYPE 影响 CSS 处理 2.FF: div 设置 margin-left, margin-right 为 auto 时已经居中, ...
- 20145234黄斐《Java程序设计》第八周
教材学习内容总结 第十四章-NIO与NIO2 NIO与IO的区别 NIO Channel继承框架 想要取得Channel的操作对象,可以使用Channels类,它定义了静态方法newChannel() ...
- svn 更新代码
SVN 更新代码 --force # 强制覆盖 /usr/bin/svn --username user --password passwd co $Code ${SvnPath}src/ # 检出整 ...