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 ...
随机推荐
- WEB前端学习代码片段记录
1.JS设计模式片段 Function.prototype.addMethod = function (name,fn) { this.prototype[name] = fn; return thi ...
- 浏览器登录Dynamics 365 CE没毛病,程序连接却报错。
摘要: 微软动态CRM专家罗勇 ,回复308或者20190308可方便获取本文,同时可以在第一间得到我发布的最新博文信息,follow me!我的网站是 www.luoyong.me . 今天我做实验 ...
- C# 利用SharpZipLib生成压缩包
本文通过一个简单的小例子简述SharpZipLib压缩文件的常规用法,仅供学习分享使用,如有不足之处,还请指正. 什么是SharpZipLib ? SharpZipLib是一个C#的类库,主要用来解压 ...
- MIUI10系统怎么样刷成开发版获得ROOT权限
小米的设备不同手机型号正常情况下小米官方论坛都提供两个不同系统,可分为稳定版和开发版,稳定版没有提供root超级权限管理,开发版中就提供了root超级权限,在很多工作的时候我们需要使用的一些功能强大的 ...
- XUnit 依赖注入
XUnit 依赖注入 Intro 现在的开发中越来越看重依赖注入的思想,微软的 Asp.Net Core 框架更是天然集成了依赖注入,那么在单元测试中如何使用依赖注入呢? 本文主要介绍如何通过 XUn ...
- keepalived+nginx负载均衡+ApacheWeb实现高可用
1.Keepalived高可用软件 Keepalived软件起初是专为LVS负载均衡软件设计的,用来管理并监控LVS集群系统中各个服务节点的状态,后来又加入了可以实现高可用的VRRP功能.因此,kee ...
- Python基础——2函数
函数 函数定义 def a(参数): 函数的参数:必选参数.默认参数.可变参数.命名关键字参数和关键字参数 a(x,y,z=11,*l,**b): a(x,y,z=11,*liat,city,job, ...
- Spring Boot 2.x 快速入门(上)HelloWorld示例
本文重点 最近决定重新实践下Spring Boot的知识体系,因为在项目中遇到的总是根据业务需求走的知识点,并不能覆盖Spring Boot完整的知识体系,甚至没有一个完整的实践去实践某个知识点.最好 ...
- Spring类型转换(Converter)
Spring的类型转换 以前在面试中就有被问到关于spring数据绑定方面的问题,当时对它一直只是朦朦胧胧的概念,最近稍微闲下来有时间看了一下其中数据转换相关的内容,把相应的内容做个记录. 下面先说明 ...
- 一篇文章搞定mongodb
一 安装 1 安装目录下新建文件夹data,etc,logs #在bin文件下启动cmd,指定数据存储的路径mongod --dbpath D:\MongoDB\data\db 2 etc文件夹中新建 ...
