AVL树
AVL树
在二叉查找树(BST)中,频繁的插入操作可能会让树的性能发生退化,因此,需要加入一些平衡操作,使树的高度达到理想的O(logn),这就是AVL树出现的背景。注意,AVL树的起名来源于两个发明者:Adel'son-Vel'skii 和 Landis。
AVL树除了具备BST树的基本特征之外,还具有一个非常重要的特点:
如果将一个节点的左、右子树的高度差定义为该节点的平衡因子,则AVL树的任意一个节点的平衡因子只有0、-1、1 三种取值。
可以采用递归的方法来判断一个BST树是不是AVL树:
typedef struct _pnode
{
int data;
int height;
struct _pnode *left;
struct _pnode *right;
} pnode; static int TreeDepth (pnode p)
{
if (!p) return ; int nLeft = TreeDepth(p->left);
int nRight = TreeDepth(p->right); return (nLeft > nRight) ? (nLeft+) : (nRight+);
} static int IsBalanced(pnode root)
{
if (NULL == root)
return ; int left = TreeDepth(root->left);
int right = TreeDepth(root->right); int diff = left - right; if (diff > || diff < -) {
return ;
} return IsBalanced(root->left) && IsBalanced(root->right);
}
递归法的代码虽然简洁,但同一个结点会被重复遍历多次,因此效率并不高。
用后序遍历的方式遍历二叉树的每一个结点,在遍历到一个结点之前我们已经遍历了它的左右子树。
只要在遍历每个结点的同时记录它的深度,就可以一边遍历一边判断每个结点是否平衡。
static int IsBalanced2(pnode root, int* pDepth)
{
int left, right; if (root == NULL) {
*pDepth = ;
return ;
} if (IsBalanced2(root->left, &left)
&& IsBalanced2(root->right, &right)) { int diff = left - right;
if (diff <= && diff >= -) {
*pDepth = + (left > right ? left : right);
return ;
}
} return ;
}
那么,AVL树是如何保证其平衡呢?当插入一个节点时,首先检查是否因插入而破坏了平衡,若破坏,则找出其中的最小不平衡子树,在保持二叉排序树特性的情况下,调整最小不平衡子树中节点之间的关系(通过左旋转和右旋转实现),以达到新的平衡。
注意:最小不平衡子树指离插入节点最近且以平衡因子的绝对值大于1的节点作为根的子树。
树的旋转
- LL: 插入一个新节点到根节点的左子树(Left)的左子树(Left),导致根节点的平衡因子由1变为2
- LR:插入一个新节点到根节点的左子树(Left)的右子树(Right),导致根节点的平衡因子由1变为2
- RL:插入一个新节点到根节点的右子树(Right)的左子树(Left),导致根节点的平衡因子由-1变为-2
- RR:插入一个新节点到根节点的右子树(Right)的右子树(Right),导致根节点的平衡因子由-1变为-2


void SingRotateLeft(pnode* &k2)
{
pnode *k1; k1 = k2->left;
k2->left = k1->right;
k1->right = k2; k2->height = Max(TreeDepth(k2->left), TreeDepth(k2->right)) + ;
k1->height = Max(TreeDepth(k1->left), k2->height) + ;
}
RR单旋转类似,

void SingRotateRight(pnode* &k2)
{
pnode *k1; k1 = k2->right;
k2->right = k1->left;
k1->left = k2; k2->height = Max(TreeDepth(k2->left), TreeDepth(k2->right))+;
k1->height = Max(TreeDepth(k1->right), k2->height)+;
}
~

void DoubleRotateLR(pnode* &k3)
{
SingRotateRight(k3->left);
SingRotateLeft(k3);
}
RL也是类似的,
void DoubleRotateRL(pnode* &k3)
{
SingRotateLeft(k3->right);
SingRotateRight(k3);
}
插入
插入的方法和二叉查找树基本一样,区别是,插入完成后需要从插入的节点开始维护一个到根节点的路径,每经过一个节点都要维持树的平衡。维持树的平衡要根据高度差的特点选择不同的旋转算法。
void insert(pnode* &node, int key)
{
// 如果节点为空,就在此节点处加入key信息
if (node == NULL) {
node = (pnode*) malloc(sizeof(pnode));
memset(node, , sizeof(pnode));
node->data = key; // 如果key小于节点的值,就继续在节点的左子树中插入key
} else if (node->data > key) { insert(node->left, key); // 如果高度之差为2的话就失去了平衡,需要旋转
if ( == TreeDepth(node->left) - TreeDepth(node->right)) { if (key < node->left->data) {
SingRotateLeft(node); } else {
DoubleRotateLR(node);
}
} //如果key大于节点的值,就继续在节点的右子树中插入key
} else if (node->data < key) { insert(node->right, key); if ( == TreeDepth(node->right)-TreeDepth(node->left)) { if (key > node->right->data) {
SingRotateRight(node); } else {
DoubleRotateRL(node);
}
} } else {
// 如果相等
printf("error: key depulicated!");
} node->height = Max(TreeDepth(node->left), TreeDepth(node->right)) + ;
}
删除
AVL树的更多相关文章
- 算法与数据结构(十一) 平衡二叉树(AVL树)
今天的博客是在上一篇博客的基础上进行的延伸.上一篇博客我们主要聊了二叉排序树,详情请戳<二叉排序树的查找.插入与删除>.本篇博客我们就在二叉排序树的基础上来聊聊平衡二叉树,也叫AVL树,A ...
- AVL树原理及实现(C语言实现以及Java语言实现)
欢迎探讨,如有错误敬请指正 如需转载,请注明出处http://www.cnblogs.com/nullzx/ 1. 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 ...
- (4) 二叉平衡树, AVL树
1.为什么要有平衡二叉树? 上一节我们讲了一般的二叉查找树, 其期望深度为O(log2n), 其各操作的时间复杂度O(log2n)同时也是由此决定的.但是在某些情况下(如在插入的序列是有序的时候), ...
随机推荐
- php 时间倒计时代码 个人写法 有好的想法的欢迎贴出来分享
$now=time(); $secondtime=$end_time-$now;//期限时间减去现在时间 剩余时间 $second=$secondtime % 60;//取余得到秒数 $nowtime ...
- [AR]Vumark(下一代条形码)
VuMark 准备知识 Vumark的说明: https://library.vuforia.com/articles/Training/VuMark https://library.vuforia. ...
- 优化IPOL网站中基于DCT(离散余弦变换)的图像去噪算法(附源代码)。
在您阅读本文前,先需要告诉你的是:即使是本文优化过的算法,DCT去噪的计算量依旧很大,请不要向这个算法提出实时运行的苛刻要求. 言归正传,在IPOL网站中有一篇基于DCT的图像去噪文章,具体的链接地址 ...
- GNU Readline 库及编程简介
用过 Bash 命令行的一定知道,Bash 有几个特性: TAB 键可以用来命令补全 ↑ 或 ↓ 键可以用来快速输入历史命令 还有一些交互式行编辑快捷键: C-A / C-E 将光标移到行首/行尾 C ...
- ngx_http_fastcgi_module模块.md
ngx_http_fastcgi_module ngx_http_fastcgi_module模块允许将请求传递到FastCGI服务器. fastcgi_bind Syntax: fastcgi_bi ...
- ActiveMQ笔记(7):如何清理无效的延时消息?
ActiveMQ的延时消息是一个让人又爱又恨的功能,具体使用可参考上篇ActiveMQ笔记(6):消息延时投递,在很多需要消息延时投递的业务场景十分有用,但是也有一个缺陷,在一些大访问量的场景,如果瞬 ...
- [LeetCode] Ugly Number 丑陋数
Write a program to check whether a given number is an ugly number. Ugly numbers are positive numbers ...
- 30秒搞定javascript作用域
引用一下js权威指南的一名话作为开场”JavaScript中的函数运行在它们被定义的作用域里,而不是它们被执行的作用域里.” javascript不存在大括号级的作用域,但他有函数作用域,也就是说变量 ...
- laravel实现数据库多库配置,读写分离配置或者多读写分离配置
'connections' => array( //默认mysql配置,访问test库 'mysql' => array( 'driver' => 'mysql', 'host' = ...
- 文件上传之——用SWF插件实现文件异步上传和头像截取
之前写过几篇文件上传,那些都不错.今天小编带领大家体会一种新的上传方法,及使用Flash插件实现文件上传. 使用Flash的好处就是可以解决浏览器兼容性问题.之前我写的一个快捷复制功能也是利用的Fla ...