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 ...
随机推荐
- 前端js 实现文件下载
https://www.zhangxinxu.com/wordpress/2017/07/js-text-string-download-as-html-json-file/ 侵删 1.H5 down ...
- 驰骋工作流引擎JFlow与activiti的对比 -总结
共同点: 1. 嵌入式的工作流引擎,降低集群复杂性. 2. 严格而灵活的流程版本控制 3. 支持多种数据库 4. 支持多种流程设计模式 5. 成熟度高的开源工作流,具有可靠的稳定性和性能. 区别: 1 ...
- LEDAPS1.3.0版本移植到windows平台----HuSr大气校正模块
这个是2012年左右放在百度空间的,谁知百度空间关闭...转移到博客园. 最近项目用到3.1.2版本的LEDAPS,新版本的使用情况会在后续文章中慢慢丰富. HuSr是将LEDAPS项目中的TM/ET ...
- iOS----------常见经典错误
最近使用cocoapods集成友盟 发现几个经典错误 1.clang: error: linker command failed with exit code 1 (use -v to see in ...
- 从零学习Flutter(一):初识Dart
Fluter是Google推出的跨平台开发App的一套框架,很多人都说,Google出品比属于金品,故,我也来凑凑热闹,Fluter是用Dart写的,所以在用Fluter之前,我们还是有必要简单了解一 ...
- 测者的性能测试手册:快速安装LoadRunner Linux上的Generator
安装和初始化 安装包 上传Linux.zip(LoadRunner Generator for Linux.zip,后台回复loadrunner获取下载地址),然后通过如下命令: unzip Linu ...
- Linux 配置本地源 (Ubuntu / CentOS)
目录 Linux local source list A. Ubuntu 1. 本地ISO 2. 制作本地源 B. CentOS 1. 本地ISO Linux local source list A. ...
- 【原】Java学习笔记013 - 阶段测试
package cn.temptation; import java.util.Scanner; public class Sample01 { public static void main(Str ...
- mysql 从一个表中查询,插入到另一个表中
insert into table1(field1) select field1 from table2; ;
- ASP.NET -- WebForm -- ScriptManager 类
ASP.NET -- WebForm -- ScriptManager 类 通过 ScriptManager 可注册随后将作为页面一部分呈现的脚本. 1. 注册并立即执行脚本. --RegisterS ...