AVL树是一种特殊的二叉查找树,其特征在于:对所有节点来说,其左子树和右子树间的高度差小于等于1。本文简要总结下AVL树的几种基本操作。

节点结构体定义

typedef struct Node_s {
int element;
struct Node_s * left, * right;
int height;
} Node;

为了突出说明核心问题,节点数据类型使用最简单的int表示;height为树的高度,叶子节点高度为0,每向上一层加1,即每个节点的深度为其左右子树最大深度加1。可使用以下几个宏定义来计算及获取深度:

#define Height(T) (T == NULL ? -1 : T->height)
#define MAX(a, b) (a > b ? a : b)
#define CalHeight(T) (T->height = MAX(Height(T->left), Height(T->right)) + 1)

AVL树的旋转

旋转操作是AVL树特有的操作,也是学习AVL树的核心,旋转的目的在于解决插入、删除等操作造成的AVL树不平衡问题。AVL树的不平衡一共只有4种情况(以插入为例说明):

  • LL:在左子树左节点进行插入
  • LR:在左子树右节点进行插入
  • RR:在右子树右节点进行插入
  • RL:在右子树左节点进行插入

示意图:



关于旋转的详细分析可参考第一篇参考资料,此处仅给出实现代码及简要思路。

LL及RR单旋转

二者是镜像操作,实现方法比较简单。

二者是镜像操作,实现方法比较简单。

Node * RotateLL (Node * T) {
Node * t = T->left; T->left = t->right;
t->right = T; CalHeight(T);
CalHeight(t); return t;
}
Node * RotateRR (Node * T) {
Node * t = T->right; T->right = t->left;
t->left = T; CalHeight(T);
CalHeight(t); return t;
}

调整完节点关系后,需要重新计算一下节点的高度。

LR及RL双旋转

二者也是镜像操作,可以视为两次单旋转的结合。

Node * RotateLR(Node * T) {
T->left = RotateRR(T->left);
return RotateLL(T);
}
Node * RotateRL(Node * T) {
T->right = RotateLL(T->right);
return RotateRR(T);
}

因为单旋转操作已经正确的调整了节点高度,双旋转中无需再调整节点高度。

常用操作

插入

一般使用递归形式,递归函数返回时,检查当前节点是否平衡,不平衡则执行旋转操作。

Node * Insert(Node * T, int val) {
/* 新插入的元素必定为叶子节点 */
if (T == NULL) {
T = (Node *)malloc(sizeof(Node));
T->element = val;
T->left = T->right = NULL;
}
/* 在左子树插入 */
else if (val < T->element) {
T->left = Insert(T->left, val);
if (Height(T->left) - Height(T->right) == 2) {
if (val < T->left->element)
T = RotateLL(T);
else
T = RotateLR(T);
}
}
/* 在右子树插入 */
else if (val > T->element) {
T->right = Insert(T->right, val);
if (Height(T->right) - Height(T->left) == 2) {
if (val > T->right->element)
T = RotateRR(T);
else
T = RotateRL(T);
}
}
/* 若元素已存在,不执行任何操作 */ /* 递归函数返回前调整节点高度 */
/* 这保证了每一层递归函数返回的节点高度都是正确的 */
/* 进而保证了整棵树的节点高度正确 */
CalHeight(T);
return T;
}

查找元素

根据二叉查找树的基本性质,可以很容易的写出查找最大元素、最小元素、任意元素的代码。

Node * FindMax(Node * T) {
while(T->right != NULL) {
T = T->right;
}
return T;
}
Node * FindMin(Node * T) {
while(T->left != NULL) {
T = T->left;
}
return T;
}
Node * Find(Node * T, int val) {
while (T != NULL) {
if (T->element == val)
break;
else if (val < T->element)
T = T->left;
else
T = T->right;
}
return T;
}

删除

删除是最复杂的操作,如果删除操作不多的话,可以考虑使用懒惰删除的策略,即增加一个标志位,表明当前节点是否被删除了。如果需要真实的删除元素,使用以下方法进行:

Node * Delete(Node * T, int val) {
/* 待删除元素在左子树中 */
if (val < T->element) {
T->left = Delete(T->left, val);
if (Height(T->right) - Height(T->left) == 2) {
if (Height(T->right->left) < Height(T->right->right))
T = RotateRR(T);
else
T = RotateRL(T);
}
}
/* 待删除元素在右子树中 */
else if (val > T->element) {
T->right = Delete(T->right, val);
if (Height(T->left) - Height(T->right) == 2) {
if (Height(T->left->right) < Height(T->left->left))
T = RotateLL(T);
else
T = RotateLR(T);
}
}
/* 删除当前节点 */
else {
/* 当前节点有两个儿子 */
/* 选择高度较大那一边进行删除,以此避免AVL树不平衡 */
if (T->left && T->right) {
/* 选择左树的话,用左树中最大节点代替当前节点,并删除最大节点原位置 */
if (Height(T->left) > Height(T->right)) {
Node * tmax = FindMax(T->left);
T->element = tmax->element;
T->left = Delete(T->left, tmax->element);
}
/* 选择右树的话,用右树中最小节点代替当前节点,并删除最小节点原位置 */
else {
Node * tmin = FindMin(T->right);
T->element = tmin->element;
T->right = Delete(T->right, tmin->element);
}
}
/* 当前节点是叶子节点或只有一个儿子,直接删除 */
else {
Node * tmp = T;
T = T->left ? T->left : T->right;
free(tmp);
}
} /* 递归返回非空节点时,需要重新计算其高度 */
if (T)
CalHeight(T);
return T;
}

基本策略和插入一样,依然是递归的进行删除,若待删除节点有两个儿子时,使用树的删除操作中的一般方法,即选择较高一侧子树中最大或最小元素代替当前元素,之后再删除那个最大或最小元素。最大或最小元素一定是叶子元素,这样之后的删除操作就会很简单,且这样的替代删除策略不会导致树的不平衡。

遍历

同样有前序、中序、后序及层序四种遍历策略,就是树的通用遍历策略,可参考二叉树的遍历算法

数据结构——AVL树的更多相关文章

  1. 数据结构-AVL树的旋转

    http://blog.csdn.net/GabrieL1026/article/details/6311339 平衡二叉树在进行插入操作的时候可能出现不平衡的情况,AVL树即是一种自平衡的二叉树,它 ...

  2. 简单数据结构———AVL树

    C - 万恶的二叉树 Crawling in process... Crawling failed Time Limit:1000MS     Memory Limit:32768KB     64b ...

  3. JAVA数据结构--AVL树的实现

    AVL树的定义 在计算机科学中,AVL树是最先发明的自平衡二叉查找树.在AVL树中任何节点的两个子树的高度最大差别为1,所以它也被称为高度平衡树.查找.插入和删除在平均和最坏情况下的时间复杂度都是.增 ...

  4. 数据结构--Avl树的创建,插入的递归版本和非递归版本,删除等操作

    AVL树本质上还是一棵二叉搜索树,它的特点是: 1.本身首先是一棵二叉搜索树.   2.带有平衡条件:每个结点的左右子树的高度之差的绝对值最多为1(空树的高度为-1).   也就是说,AVL树,本质上 ...

  5. 再回首数据结构—AVL树(一)

    前面所讲的二叉搜索树有个比较严重致命的问题就是极端情况下当数据以排序好的顺序创建搜索树此时二叉搜索树将退化为链表结构因此性能也大幅度下降,因此为了解决此问题我们下面要介绍的与二叉搜索树非常类似的结构就 ...

  6. 再回首数据结构—AVL树(二)

    前面主要介绍了AVL的基本概念与结构,下面开始详细介绍AVL的实现细节: AVL树实现的关键点 AVL树与二叉搜索树结构类似,但又有些细微的区别,从上面AVL树的介绍我们知道它需要维护其左右节点平衡, ...

  7. 第三十二篇 玩转数据结构——AVL树(AVL Tree)

          1.. 平衡二叉树 平衡二叉树要求,对于任意一个节点,左子树和右子树的高度差不能超过1. 平衡二叉树的高度和节点数量之间的关系也是O(logn) 为二叉树标注节点高度并计算平衡因子 AVL ...

  8. Java数据结构——AVL树

    AVL树(平衡二叉树)定义 AVL树本质上是一颗二叉查找树,但是它又具有以下特点:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树,并且拥有自平衡机制.在AV ...

  9. 数据结构 - AVL 树

    简介 基本概念 AVL 树是最早被发明的自平衡的二叉查找树,在 AVL 树中,任意结点的两个子树的高度最大差别为 1,所以它也被称为高度平衡树,其本质仍然是一颗二叉查找树. 结合二叉查找树,AVL 树 ...

  10. 数据结构-AVL树

    实现: #ifndef AVL_TREE_H #define AVL_TREE_H #include "dsexceptions.h" #include <iostream& ...

随机推荐

  1. 本地MinIO存储服务Java远程调用上传文件

    MinIO是一款高性能.分布式的对象存储系统,它可以100%的运行在标准硬件上,即X86等低成本机器也能够很好的运行MinIO.它的优点包括高性能.高可用性.易于部署和管理.支持多租户等. Cpola ...

  2. C语言假设今天是星期日,编写一个程序,求2019天后是星期几。

    #include<stdio.h> void main() { int n = 2019, d;//定义变量和常量 d = n % 7;//计算余数 switch (d)//选择星期 { ...

  3. BI到底是什么,是否所有企业都适合上BI?

    商业智能(Business Intelligence)的概念 商业智能(BI)是一种综合性的数据分析和决策支持系统,旨在帮助企业从海量的数据中提取有价值的信息,并将其转化为洞察力.报告和可视化呈现,以 ...

  4. 终结篇:==和equals有什么区别?

    == 和 equals 有什么区别?这个问题本身不难,但是被问到的频率很高,且大部分人的回答都不够全面,让人听了有种"恨铁不成钢"的感觉,所以今天咱们就来好好聊聊这个问题. 1.典 ...

  5. 聊聊GLM基座模型的理论知识

    概述 大模型有两个流程:预训练和推理. 预训练是在某种神经网络模型架构上,导入大规模语料数据,通过一系列的神经网络隐藏层的矩阵计算.微分计算等,输出权重,学习率,模型参数等超参数信息. 推理是在预训练 ...

  6. 10 个免费的 AI 图片生成工具分享

    原文: https://openaigptguide.com/ai-picture-generator/ 在人工智能(AI)图像生成技术的推动下,各类AI图片生成网站如雨后春笋般涌现,为我们的日常生活 ...

  7. 经典卷积神经网络LeNet&AlexNet&VGG

    LeNet LeNet-5是一种经典的卷积神经网络结构,于1998年投入实际使用中.该网络最早应用于手写体字符识别应用中.普遍认为,卷积神经网络的出现开始于LeCun等提出的LeNet网络,可以说Le ...

  8. Linux磁盘专题-linux文件系统详解

    这可是我几年前的杰作笔记呀.....当初手写计算都会,现在忘光光.... 物理硬盘Block的概念和作用 硬盘底层一次IO就是读.写一次扇区,一个扇区默认是512Byte. 读写大量文件如果以扇区为单 ...

  9. 数字孪生结合GIS能够在公共交通领域作出什么贡献?

    数字孪生结合地理信息系统(GIS)在公共交通领域具有潜在的重大贡献,这种结合可以帮助城市更高效地规划.运营和改进公共交通系统.以下是一些关键方面的讨论,以说明数字孪生和GIS在这一领域的作用: 数字孪 ...

  10. Windows Server 2012 R2 远程桌面服务部署指南

    著作权归作者所有:来自51CTO博客作者mabofeng的原创作品,请联系作者获取转载授权,否则将追究法律责任 01-Windows Server 2012 R2 远程桌面服务部署指南文章来源:htt ...