AVL树探秘
本文首发于我的公众号 Linux云计算网络(id: cloud_dev) ,专注于干货分享,号内有 10T 书籍和视频资源,后台回复 「1024」 即可领取,欢迎大家关注,二维码文末可以扫。
一、AVL树
AVL树是一种平衡查找树,在前面的两篇文章:二叉搜索树 和 红黑树 中都提到过。由于二叉搜索树在某些特殊情况下是不平衡的(任意一个结点深度过大),因此其一些动态集合操作在最坏情况下的时间复杂度为O(n)。因此提出一些对二叉搜索树效率改进的树结构使最坏时间复杂度降为O(lgn),AVL树和红黑树就是其中的代表,除此之外,还有一些如AA-tree、B-tree、2-3-tree等。使不平衡树变平衡最关键的是找到“平衡条件”,我们已经在前面一篇文章中详述了红黑树的平衡条件是:对节点进行着色,并约束从根节点到任何叶子节点的长度,其中,约定了5条规定,稍显复杂。而AVL树的平衡条件则显得格外简单:只用保证左右子树的高度不超过1即可。
二、AVL树的实现
1、数据结构
节点类:因为需要控制节点的高度,所以高度是一个属性。指针域包括left、right,parent可以包括也可以不要,本文的实现中,我们包括parent。
struct AVLNode {
AVLNode *Parent;
AVLNode *Left;
AVLNode *Right;
int m_nHeight;
int m_nValue;
};
2、节点的平衡
当插入新的节点或者删除节点时,会导致树的不平衡,即其中有节点的左右子树的高度相差>1,这个时候就需要调节树使之平衡。可能出现不平衡的情况总共有以下几种:
////////////////////////////////////////
a a a a
/ / \ \
b b b b
/ \ / \
c c c c
LL LR RL RR
//////////////////////////////////////
总共就只会出现这四种情况,对这四种情况的分类很多书上有各自的说法。其中1、4和2、3是对称的,我们用LL、LR、RL、RR来分别表示,要使这几种情况平衡,我们只用做简单的旋转操作就OK了,针对1、4,有的说法是做单旋,有的说法是外旋,而2、3,则做双旋或内旋,不管是哪种说法,其本质是不变的。在我们的实现中,采用单旋和双旋,双旋就是做两次单旋:
//单旋
void _LeftRotation( AVLNode *node );
void _RightRotation( AVLNode *node ); //双旋
void _LeftRightRotation( AVLNode *node );
void _RightLeftRotation( AVLNode *node );
3、平衡的修复
在插入节点和删除节点的时候,会破坏树的平衡条件,这个时候就需要修复。我们采用尽可能少地改动原有代码的原则来修复,这个原则和红黑树的修复操作是一致的,即插入和删除操作我们依然沿用二叉搜索树的实现,只在后面添加修复的代码即可。
如何修复?首先,插入和删除会破坏节点的高度,所以,应更新结点的高度;其次,插入和删除破坏了树中某些节点的平衡,所以,应针对上面四种情况分别平衡节点。所以,这里就需要两个函数:一个更新结点高度的函数UpdateHeight( AVLNode *node );一个平衡节点的函数: BalanceNode( AVLNode *node )。
void AVLTree::_UpdateHeight( AVLNode *node )
{
AVLNode *l = node->Left, *r = node->Right; if ( l && r )
node->m_nHeight = max( l->m_nHeight, r->m_nHeight ) + ;
else if ( l )
node->m_nHeight = l->m_nHeight + ;
else if ( r )
node->m_nHeight = r->m_nHeight + ;
else node->m_nHeight = ;
}
//////////////////////////////////////////////////////////////////////////
// a a a a
// / / \ \
// b b b b
// / \ / \
// c c c c
// LL LR RL RR
//////////////////////////////////////////////////////////////////////////
void AVLTree::_BalanceNode( AVLNode *node )
{
int nBlance = _GetBalanceFactor( node );
if ( nBlance > ) { //L
//(1)
//if ( _GetBalanceFactor( node->Left ) < 0 ) //LR
// _LeftRightRotation( node ); //双旋
//else _RightRotation( node ); //LL //单旋 //(2)
if ( _GetBalanceFactor( node->Left ) < )
_LeftRotation( node->Left );
_RightRotation( node );
}
if ( nBlance < - ) { //R
if ( _GetBalanceFactor( node ) > ) { //RL
_RightRotation( node->Right );
}
_LeftRotation( node );
}
} //平衡因子(左右子树的高度差)
int AVLTree::_GetBalanceFactor( AVLNode *node )
{
AVLNode *l = node->Left, *r = node->Right; if ( l && r )
return l->m_nHeight - r->m_nHeight;
else if ( l )
return l->m_nHeight + ;
else if ( r )
return -r->m_nHeight - ;
else return ;
}
基本上该注意的点都提到了,下面附上详细代码实现:
#ifndef __AVL_TREE_H_
#define __AVL_TREE_H_ class AVLTree
{
private:
struct AVLNode {
AVLNode *Parent;
AVLNode *Left;
AVLNode *Right;
int m_nHeight;
int m_nValue;
};
public:
AVLTree(AVLNode *root = NULL):m_pRoot(root) {}
~AVLTree() {
_RecursiveDeleteNode(m_pRoot);
} bool Search( const int search_value ) const;
bool Insert( const int value );
bool Delete( const int delete_value ); void Print() const; private:
void _RecursiveDeleteNode(AVLNode *node) {
if ( node ) {
_RecursiveDeleteNode( node->Left );
_RecursiveDeleteNode( node->Right );
delete node;
}
node = NULL;
} void _DeleteNode( AVLNode *delete_node );
void _Delete_Transplant( AVLNode *unode, AVLNode *vnode );
void _InsertNode( const int insert_value );
AVLNode * _SearchNode( AVLNode *node, const int search_value ) const; //单旋
void _LeftRotation( AVLNode *node );
void _RightRotation( AVLNode *node ); //双旋
void _LeftRightRotation( AVLNode *node );
void _RightLeftRotation( AVLNode *node ); AVLNode* Minimum( AVLNode *node ); //树高
int _Height ( AVLNode *node );
void _UpdateHeight( AVLNode *node );
//平衡因子
int _GetBalanceFactor( AVLNode *node );
//平衡失去平衡的节点
void _BalanceNode( AVLNode *node ); void _Print ( AVLNode *node ) const; private:
AVLNode *m_pRoot; };
#endif//__AVL_TREE_H_
#include <iostream>
#include <algorithm>
using namespace std; #include "AVL_Tree.h" bool AVLTree::Search( const int search_value ) const
{
return _SearchNode( m_pRoot, search_value ) != NULL;
} AVLTree::AVLNode * AVLTree::_SearchNode( AVLNode *node, const int search_value ) const
{
while ( node && search_value != node->m_nValue ) {
if ( search_value < node->m_nValue )
node = node->Left;
else
node = node->Right;
}
return node;
} bool AVLTree::Insert( const int value )
{
//该值已存在
if ( Search( value ) )
return false;
else {
_InsertNode( value );
return true;
}
} void AVLTree::_InsertNode( const int insert_value )
{
AVLNode *node = m_pRoot;
AVLNode *temp_node = NULL; //先找到待插入节点的位置
while ( node ) {
temp_node = node;
node = ( insert_value < node->m_nValue )?node->Left:node->Right;
} AVLNode *insert_node = new AVLNode();
insert_node->m_nValue = insert_value; insert_node->Parent = temp_node; //空树
if ( temp_node == NULL )
m_pRoot = insert_node;
else {
if ( insert_value < temp_node->m_nValue )//左子树
temp_node->Left = insert_node;
else
temp_node->Right = insert_node; //右子树
} //更新插入节点的高度和平衡节点
while ( insert_node ) {
_UpdateHeight( insert_node );
_BalanceNode( insert_node );
insert_node = insert_node->Parent;
}
} bool AVLTree::Delete( const int delete_value )
{
AVLNode *delete_node = _SearchNode( m_pRoot, delete_value ); //节点不存在
if ( delete_node == NULL )
return false;
else {
_DeleteNode( delete_node );
return true;
}
} void AVLTree::_DeleteNode( AVLNode *delete_node )
{
if ( delete_node->Left == NULL )
_Delete_Transplant( delete_node, delete_node->Right );
else if ( delete_node->Right == NULL )
_Delete_Transplant( delete_node, delete_node->Left );
else {
AVLNode *min_node = Minimum( delete_node->Right );
if ( min_node->Parent != delete_node ) {
_Delete_Transplant( min_node, min_node->Right );
min_node->Right = delete_node->Right;
min_node->Right->Parent = min_node;
}
_Delete_Transplant( delete_node, min_node );
min_node->Left = delete_node->Left;
min_node->Left->Parent = min_node;
} //更新结点的高度和平衡节点
while ( delete_node ) {
_UpdateHeight( delete_node );
_BalanceNode( delete_node );
delete_node = delete_node->Parent;
}
} void AVLTree::_Delete_Transplant( AVLNode *unode, AVLNode *vnode )
{
if ( unode->Parent == NULL )
m_pRoot = vnode;
else if ( unode == unode->Parent->Left )
unode->Parent->Left = vnode;
else
unode->Parent->Right = vnode;
if ( vnode )
vnode->Parent = unode->Parent;
} AVLTree::AVLNode* AVLTree::Minimum( AVLNode *node )
{
while ( node->Left )
node = node->Left;
return node;
} //树高
int AVLTree::_Height( AVLNode *node )
{
return node->m_nHeight;
} //平衡因子(左右子树的高度差)
int AVLTree::_GetBalanceFactor( AVLNode *node )
{
AVLNode *l = node->Left, *r = node->Right; if ( l && r )
return l->m_nHeight - r->m_nHeight;
else if ( l )
return l->m_nHeight + ;
else if ( r )
return -r->m_nHeight - ;
else return ;
} void AVLTree::_UpdateHeight( AVLNode *node )
{
AVLNode *l = node->Left, *r = node->Right; if ( l && r )
node->m_nHeight = max( l->m_nHeight, r->m_nHeight ) + ;
else if ( l )
node->m_nHeight = l->m_nHeight + ;
else if ( r )
node->m_nHeight = r->m_nHeight + ;
else node->m_nHeight = ;
} //////////////////////////////////////////////////////////////////////////
// a a a a
// / / \ \
// b b b b
// / \ / \
// c c c c
// LL LR RL RR
//////////////////////////////////////////////////////////////////////////
void AVLTree::_BalanceNode( AVLNode *node )
{
int nBlance = _GetBalanceFactor( node );
if ( nBlance > ) { //L
//(1)
//if ( _GetBalanceFactor( node->Left ) < 0 ) //LR
// _LeftRightRotation( node ); //双旋
//else _RightRotation( node ); //LL //单旋 //(2)
if ( _GetBalanceFactor( node->Left ) < )
_LeftRotation( node->Left );
_RightRotation( node );
}
if ( nBlance < - ) { //R
if ( _GetBalanceFactor( node ) > ) { //RL
_RightRotation( node->Right );
}
_LeftRotation( node );
}
} //单旋
//左旋
void AVLTree::_LeftRotation( AVLNode *node )
{
if ( node->Right == NULL )
return; AVLNode *temp_node = node->Right; //补
node->Right = temp_node->Left;
if ( temp_node->Left )
temp_node->Left->Parent = node; //提
temp_node->Parent = node->Parent;
if ( node->Parent == NULL )
m_pRoot = temp_node;
else if ( node == node->Parent->Left )
node->Parent->Left = temp_node;
else node->Parent->Right = temp_node; //降
temp_node->Left = node;
node->Parent = temp_node; _UpdateHeight( node );
_UpdateHeight( temp_node );
} //右旋
void AVLTree::_RightRotation( AVLNode *node )
{
if ( node->Left == NULL )
return; AVLNode *temp_node = node->Left; //补
node->Left = temp_node->Right;
if ( temp_node->Right )
temp_node->Right->Parent = node; //提
temp_node->Parent = node->Parent;
if ( node->Parent == NULL )
m_pRoot = temp_node;
else if ( node == node->Parent->Left )
node->Parent->Left = temp_node;
else node->Parent->Right = temp_node; //降
temp_node->Right = node;
node->Parent = temp_node; _UpdateHeight( node );
_UpdateHeight( temp_node );
} //双旋
//LR
void AVLTree::_LeftRightRotation( AVLNode *node )
{
_LeftRotation( node->Left );
_RightRotation( node );
} //RL
void AVLTree::_RightLeftRotation( AVLNode *node )
{
_RightRotation( node->Right );
_RightRotation( node );
} void AVLTree::Print() const
{
_Print(m_pRoot);
}
//打印
void AVLTree::_Print ( AVLNode *node ) const
{
if ( node->Parent == NULL )
cout << "root: " << node->m_nValue << endl;
else if ( node == node->Parent->Left )
cout << "left: " << node->m_nValue << ", parent: " << node->Parent->m_nValue << endl;
else cout << "right: " << node->m_nValue << ", parent: " << node->Parent->m_nValue << endl;
if ( node->Left )
_Print( node->Left );
if ( node->Right )
_Print( node->Right );
} int main()
{
AVLTree al;
for (int i = ; i < ; i ++) {
al.Insert(i); }
al.Print();
for (int i = ; i < ; i += ) {
al.Delete(i);
al.Print();
}
return ;
}
我的公众号 「Linux云计算网络」(id: cloud_dev),号内有 10T 书籍和视频资源,后台回复 「1024」 即可领取,分享的内容包括但不限于 Linux、网络、云计算虚拟化、容器Docker、OpenStack、Kubernetes、工具、SDN、OVS、DPDK、Go、Python、C/C++编程技术等内容,欢迎大家关注。
AVL树探秘的更多相关文章
- 算法与数据结构(十一) 平衡二叉树(AVL树)
今天的博客是在上一篇博客的基础上进行的延伸.上一篇博客我们主要聊了二叉排序树,详情请戳<二叉排序树的查找.插入与删除>.本篇博客我们就在二叉排序树的基础上来聊聊平衡二叉树,也叫AVL树,A ...
- AVL树原理及实现(C语言实现以及Java语言实现)
欢迎探讨,如有错误敬请指正 如需转载,请注明出处http://www.cnblogs.com/nullzx/ 1. AVL定义 AVL树是一种改进版的搜索二叉树.对于一般的搜索二叉树而言,如果数据恰好 ...
- AVL树
AVL树 在二叉查找树(BST)中,频繁的插入操作可能会让树的性能发生退化,因此,需要加入一些平衡操作,使树的高度达到理想的O(logn),这就是AVL树出现的背景.注意,AVL树的起名来源于两个发明 ...
- AVL树的平衡算法(JAVA实现)
1.概念: AVL树本质上还是一个二叉搜索树,不过比二叉搜索树多了一个平衡条件:每个节点的左右子树的高度差不大于1. 二叉树的应用是为了弥补链表的查询效率问题,但是极端情况下,二叉搜索树会无限接近 ...
- 【数据结构】平衡二叉树—AVL树
(百度百科)在计算机科学中,AVL树是最先发明的自平衡二叉查找树.在AVL树中任何节点的两个子树的高度最大差别为一,所以它也被称为高度平衡树.查找.插入和删除在平均和最坏情况下都是O(log n).增 ...
- 数据结构图文解析之:AVL树详解及C++模板实现
0. 数据结构图文解析系列 数据结构系列文章 数据结构图文解析之:数组.单链表.双链表介绍及C++模板实现 数据结构图文解析之:栈的简介及C++模板实现 数据结构图文解析之:队列详解与C++模板实现 ...
- 数据结构之平衡二叉树(AVL树)
平衡二叉树(AVL树)定义如下:平衡二叉树或者是一棵空树,或者是具有以下性质的二叉排序树: (1)它的左子树和右子树的高度之差绝对值不超过1: (2)它的左子树和右子树都是平衡二叉树. AVL树避免了 ...
- PAT树_层序遍历叶节点、中序建树后序输出、AVL树的根、二叉树路径存在性判定、奇妙的完全二叉搜索树、最小堆路径、文件路由
03-树1. List Leaves (25) Given a tree, you are supposed to list all the leaves in the order of top do ...
- 论AVL树与红黑树
首先讲解一下AVL树: 例如,我们要输入这样一串数字,10,9,8,7,15,20这样一串数字来建立AVL树 1,首先输入10,得到一个根结点10 2,然后输入9, 得到10这个根结点一个左孩子结点9 ...
随机推荐
- ionic3 Alert组件的使用方法
html页面 <button ion-button color="danger" class="button-block button-round-ios" ...
- Java新知识系列 八
什么是死锁,死锁的原因和必要条件: []什么是死锁,死锁的原因和必要条件: 死锁:死锁的原因在于进程在等待其它进程占有的某些资源,而自身的资源又被其它进程等待着,造成了死循环. 出现死锁的 ...
- 如何关闭tornado.web的Application
研究热更新Python程序时,需要将已有的HTTP服务器重启. 我的HTTP服务器是用tornado.web.Application生成的,这样很简单: import tornado.web weba ...
- JHipster技术栈定制 - JHipster Registry配置信息加密
本文说明了如何开启和使用JHipster-Registry的加解密功能. 1 整体规划 1.1 名词说明 名词 说明 备注 对称加密 最快速.最简单的一种加密方式,加密(encryption)与解密( ...
- UPUPW配置
UPUPW下载地址 https://sourceforge.net/projects/upupw/files/ANK/?tdsourcetag=s_pctim_aiomsg 配置 在这里插入图片描述
- C#的自动拼接Sql语句Insert方法及思路
思路: 1.想想插入语句,大概是这样的一个框架:INSERT INTO 表名 (数据库列名) values (值) 2.这里要3个变量是不固定的,分别是:表名.数据库列名.值: a.表名我们这里很容易 ...
- Dijango学习_01_pycharm创建应用
一.当初在学dijango的时候,网上的教程非常的杂且多,对于؏؏☝ᖗ乛◡乛ᖘ☝؏؏我们这种初入虎门的小白来说有太多误区 (其实是大佬的操作着实对小白不太友好,原谅我个萌新..2333..) 二.pi ...
- 安装指定版本的docker服务
参考博客:Docker CE 镜像源站 参考博客:docker启动异常driver not supported 1. 说明 之前部署docker服务的时候都是安装最新的docker版本,并使用dock ...
- SQLServer删除数据列
删除数据列 开发或者生产过程中多建.错误或者重复的数据列需要进行删除操作. 使用SSMS数据库管理工具删除数据列 方式一 1.打开数据库->选择数据表->展开数据表->展开数据列-& ...
- 我的Spark SQL单元测试实践
最近加入一个Spark项目,作为临时的开发人员协助进行开发工作.该项目中不存在测试的概念,开发人员按需求进行编码工作后,直接向生产系统部署,再由需求的提出者在生产系统检验程序运行结果的正确性.在这种原 ...
