数据结构-AVL树

目录

什么是 AVL 树

ASL 度量查找效率

结构体定义

平衡调整

调整类型

左旋和右旋

右旋

左旋

左、右平衡调整

左平衡调整

右平衡调整

插入数据

模拟建立 AVL 树


什么是 AVL 树

        二叉排序树的形状取决于数据集,当二叉树的高度越小、结构越合理,搜索的性能就越好,时间复杂度 O(log2n)。G. M. Adelson-Velsky 和 E. M. Landis 在1962年的论文《An algorithm for the organization of information》中发表了一种名为 AVL 树的数据结构,它就能很好地解决这个问题。AVL 树具有以下 2 个性质:

  1. 左子树和右子树的深度之差的绝对值不超过 1;
  2. 左子树和右子树通通都是 AVL 树。

        其中为了度量左右子树的深度之差,我们引入平衡因子 (BF)"的概念。例如下面的二叉搜索树的平衡因子为:

        对于一棵 AVL 树,里面的所有结点的平衡因子只能取值于:-1、0、1


ASL 度量查找效率

        为了更好地理解 AVL 树,请认真观察下面 2 个二叉搜索树,我们发现第二个二叉搜索树是 AVL 树,树的高度更小,查找的比较次数也更少,效率更高。


        现在计算一下该 AVL 树的 ASL:

ASL(成功):(1 + 2 × 2 + 3 × 4) ÷ 6 = 17/6
ASL(失败):4 × 8 ÷ 8 = 4

        和二叉搜索树对比,发现无论是成功还是失败的 ASL,AVL 树的都较小,说明效率更高。


结构体定义

typedef struct BiTNode
{
      int data;
      int bf;  //平衡因子
      struct BiTNode *lchild, *rchild;
}BiTNode, *BiTree;

平衡调整

调整类型

        由于二叉搜索树的结点是一个一个插入的,在插入时可能会造成结点的平衡因子的绝对值超过 1。造成失衡一共有 4 种情况:RR 型、LL 型、RL 型、LR 型,如图所示:

        虽然有 4 种情况,但是需要遵循的原则是一样的——在尽可能减小高度的前提下,保持二叉搜索树的性质。下面就看一下 4 种情况的调整示意图,不难发现都是遵循这个原则进行调整的。


        需要注意的是,当有多个结点失衡时,需要选择最小失衡子树来调整。


左旋和右旋

右旋

        右旋可以形象地理解为把最上面的结点掰下来,这种操作称之为“右旋”。右旋操作后指向新的根结点,即操作之前的根结点的左结点。

void R_Rotate(BiTree &T)
{ 
      BiTree L;      //L 表示 T 的左子树
      L = T->lchild;    //L 指向 T 的左子树 
      T->lchild = L->rchild;      //T 的左子树更改为 T 的左子树的右子树  
      L->rchild = T;      //T 的左子树的右子树修改为 T
      T = ptr;      //根结点修改为右旋之前 T 的左子树
}

左旋

        对于 RR 型的调整,可以形象地理解为把最上面的结点掰下来,这种操作称之为“左旋”。左旋操作后指向新的根结点,即操作之前的根结点的右结点。

void L_Rotate(BiTree &T)
{ 
      BiTree R;      //R 表示 T 的右子树
      R = T->rchild;        //R 指向 T 的右子树  
      T->rchild = R->lchild;      //T 的右子树更改为 T 的右子树的左子树  
      R->lchild = T;      //T 的右子树的左子树修改为 T
      T = R;      //根结点修改为左旋之前 T 的右子树
}

左、右平衡调整

左平衡调整

        这个函数为左平衡旋转,将 RL 型和 LL 型进行判断和操作。需要考虑涉及结点所连接的子树,对每个结点的 BF 都进行修正。

void LeftBalance(BiTree &T)
{ 
    BiTree L;      //L 表示 T 的左子树
    BiTree Lr;      //Lr 表示 L 的右子树

    L = T->lchild;      //L 指向 T 的左结点 
    switch(L->bf)      //根据 T 的左子树的 BF 作相应平衡处理
    {
        case 1:          //LL 型,新结点插入在 T 的左结点的左子树
        {
            T->bf = L->bf = 0;      //修正 BF 均为 0
            R_Rotate(T);      //右旋操作
            break;
        }
        case -1:          //RL 型,新结点插入在 T 的左结点的右子树,要做双旋操作
        {
            Lr = L->rchild;       //Lr 指向示 L 的右结点
            switch(Lr->bf)      //修正 T 及其左结点的平衡因子
            { 
                case 1:            //Lr 平衡因子为 1
                {
                    T->bf = -1;
                    L->bf = 0;
                    break;
                }
                case 0:            //Lr 平衡因子为 0
                {
                    T->bf = L->bf = 0;
                    break;
                }
                case -1:            //Lr 平衡因子为 -1
                {
                    T->bf = 0;
                    L->bf = 1;
                    break;
                }
            }
            Lr->bf = 0;      //修正 Lr 平衡因子为 0
            L_Rotate(T->lchild);       //对 T 的左子树作左旋操作  
            R_Rotate(T);      //对 T 作右旋操作
            break;
        }
    }
}

右平衡调整

        这个函数为右平衡旋转,将 LR 型和 RR 型进行判断和操作。需要考虑涉及结点所连接的子树,对每个结点的 BF 都进行修正。

void RightBalance(BiTree &T)
{ 
    BiTree R;      //R 表示 T 的右子树
    BiTree Rl;      //Rl 表示 R 的左子树

    R = T->rchild;      //R 指向 T 的右结点 
    switch(R->bf)      //根据 T 的右子树的 BF 作相应平衡处理
    {
        case -1:          //RR 型,新结点插入在 T 的右结点的右子树
        {
            T->bf = R->bf = 0;      //修正 BF 均为 0
            L_Rotate(T);      //左旋操作
            break;
        }
        case 1:          //LR 型,新结点插入在 T 的右结点的左子树,要做双旋操作
        {
            Rl = R->lchild;       //Rl 指向 R 的左结点
            switch(Rl->bf)      //修正 T 及其右结点的平衡因子
            { 
                case -1:            //Rl 平衡因子为 -1
                {
                    T->bf = 1;
                    R->bf = 0;
                    break;
                }
                case 0:            //Rl 平衡因子为 0
                {
                    T->bf = R->bf = 0;
                    break;
                }
                case 1:            //Rl 平衡因子为 1
                {
                    T->bf = 0;
                    R->bf = -1;
                    break;
                }
            }
            Rl->bf = 0;      //修正 Rl 平衡因子为 0
            R_Rotate(T->rchild);       //对 T 的右子树作右旋操作  
            L_Rotate(T);      //对 T 作左旋操作
            break;
        }
    }
}

插入数据

        要插入一个数据,首先需要判断这个数据是否已存在,若在 AVL 树中不存在要插入的数据,则执行插入操作。函数将通过递归找到合适的插入位置,如果在插入时出现失去平衡的情况,要进行对应的平衡旋转处理。

bool InsertAVL(BiTree &T, int e, bool &taller) {
    // taller 表示树的高度是否发生变化
    if (T == NULL) { // 若传入的 T 为空树,将创建根结点
        T = new BiTNode;
        T->data = e;
        T->bf = 0;
        T->lchild = T->rchild = NULL;
        taller = true; // 表示树的高度是否发生变化
    } else {
        if (e == T->data) { // 和 e 有相同数据的结点已存在
            taller = false;
            return false;
        }
        if (e < T->data) { // 进入左子树搜索插入位置
            if (InsertAVL(T->lchild, e, taller) == false) { // 结点已存在
                return false;
            }
            if (taller == true) { // 数据插入 T 的左子树中并且高度发生变化
                switch (T->bf) { // 检查 T 的 BF 判断是否调整
                    case 1: // 原本左子树比右子树高,需要进行左平衡处理
                        LeftBalance(T); // 左平衡处理
                        taller = false;
                        break;
                    case 0: // 原本左、右子树等高,仍保持平衡,修正 BF
                        T->bf = 1;
                        taller = true;
                        break;
                    case -1: // 原本右子树比左子树高,仍保持平衡,修正 BF
                        T->bf = 0;
                        taller = false;
                        break;
                }
            }
        } else { // 进入右子树搜索插入位置
            if (InsertAVL(T->rchild, e, taller) == false) { // 结点已存在
                return false;
            }
            if (taller == true) { // 数据插入 T 的左子树中并且高度发生变化
                switch (T->bf) { // 检查 T 的 BF 判断是否调整
                    case 1: // 原本右子树比左子树高,仍保持平衡,修正 BF
                        T->bf = 0;
                        taller = false;
                        break;
                    case 0: // 原本左、右子树等高,仍保持平衡,修正 BF
                        T->bf = -1;
                        taller = true;
                        break;
                    case -1: // 原本右子树比左子树高,需要进行右平衡处理
                        RightBalance(T);
                        taller = false;
                        break;
                }
            }
        }
    }
    return true;
}

模拟建立 AVL 树

        按照整数序列 {4,5,7,2,1,3,6} 依次插入的顺序构造相应平衡二叉树。
        首先结点 4 加入 AVL 树成为根结点。

        结点 5 加入 AVL 树。

        结点 7 加入 AVL 树,此时结点 4 的平衡因子为 -2,需要进行调整。


        进行 RR 型调整。

        结点 2 加入 AVL 树。

        结点 1 加入 AVL 树,此时结点 4 的平衡因子为 2,需要进行调整。

        进行 LL 型调整。

        结点 3 加入 AVL 树,此时结点 5 的平衡因子为 2,需要进行调整。

        进行 LR 型调整。

        结点 6 加入 AVL 树,此时结点 5 的平衡因子为 -2,需要进行调整。

        进行 RL 型调整。

        到此为止,AVL 树建立完毕。完整的代码实现如下:

#include <stdio.h>
#include <stdlib.h>

// AVL树的节点结构
typedef struct BiTNode {
    int data;
    int bf; // 平衡因子
    struct BiTNode *lchild, *rchild;
} BiTNode, *BiTree;

// 右旋转
void R_Rotate(BiTree *P) {
    BiTree L;
    L = (*P)->lchild;
    (*P)->lchild = L->rchild;
    L->rchild = (*P);
    *P = L;
}

// 左旋转
void L_Rotate(BiTree *P) {
    BiTree R;
    R = (*P)->rchild;
    (*P)->rchild = R->lchild;
    R->lchild = (*P);
    *P = R;
}

// 左平衡旋转处理
void LeftBalance(BiTree *T) {
    BiTree L, Lr;
    L = (*T)->lchild;
    switch (L->bf) {
        case 1:
            (*T)->bf = L->bf = 0;
            R_Rotate(T);
            break;
        case -1:
            Lr = L->rchild;
            switch (Lr->bf) {
                case 1:
                    (*T)->bf = -1;
                    L->bf = 0;
                    break;
                case 0:
                    (*T)->bf = L->bf = 0;
                    break;
                case -1:
                    (*T)->bf = 0;
                    L->bf = 1;
                    break;
            }
            Lr->bf = 0;
            L_Rotate(&(*T)->lchild);
            R_Rotate(T);
    }
}

// 右平衡旋转处理
void RightBalance(BiTree *T) {
    BiTree R, Rl;
    R = (*T)->rchild;
    switch (R->bf) {
        case -1:
            (*T)->bf = R->bf = 0;
            L_Rotate(T);
            break;
        case 1:
            Rl = R->lchild;
            switch (Rl->bf) {
                case 1:
                    (*T)->bf = 0;
                    R->bf = -1;
                    break;
                case 0:
                    (*T)->bf = R->bf = 0;
                    break;
                case -1:
                    (*T)->bf = 1;
                    R->bf = 0;
                    break;
            }
            Rl->bf = 0;
            R_Rotate(&(*T)->rchild);
            L_Rotate(T);
    }
}

// 插入节点到AVL树中
int InsertAVL(BiTree *T, int key, int *taller) {
    if (!*T) {
        *T = (BiTree)malloc(sizeof(BiTNode));
        (*T)->data = key;
        (*T)->lchild = (*T)->rchild = NULL;
        (*T)->bf = 0;
        *taller = 1;
    } else {
        if (key == (*T)->data) { // 树中已存在相同的节点
            *taller = 0;
            return 0;
        }
        if (key < (*T)->data) { // 插入到左子树
            if (!InsertAVL(&((*T)->lchild), key, taller))
                return 0;
            if (*taller) { // 高度增加了
                switch ((*T)->bf) {
                    case 1:
                        LeftBalance(T);
                        *taller = 0;
                        break;
                    case 0:
                        (*T)->bf = 1;
                        *taller = 1;
                        break;
                    case -1:
                        (*T)->bf = 0;
                        *taller = 0;
                        break;
                }
            }
        } else { // 插入到右子树
            if (!InsertAVL(&((*T)->rchild), key, taller))
                return 0;
            if (*taller) { // 高度增加了
                switch ((*T)->bf) {
                    case 1:
                        (*T)->bf = 0;
                        *taller = 0;
                        break;
                    case 0:
                        (*T)->bf = -1;
                        *taller = 1;
                        break;
                    case -1:
                        RightBalance(T);
                        *taller = 0;
                        break;
                }
            }
        }
    }
    return 1;
}

int main() {
    int count;      // 数据元素的个数
    BiTree T = NULL;
    int taller, flag; // taller 反映 T 的高度是否变化
    int a_num;      // 暂存输入数据

    scanf("%d", &count);
    for (int i = 0; i < count; i++) {
        scanf("%d", &a_num);
        flag = InsertAVL(&T, a_num, &taller); // 向 AVL 树中插入 a_num
    }
    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/590279.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

如何在iOS设备(iPhone,iPad等)上恢复丢失的照片

如果你像现代90%的人一样拥有智能手机&#xff0c;那么你很可能使用口袋里的微型电脑拍摄大部分&#xff08;如果不是全部&#xff09;照片&#xff0c;而不是标准的傻瓜相机或数码单反相机。 像任何数字设备一样&#xff0c;存储和保存这些照片可能是一个变化无常的过程&…

MySQL商城数据库88张表结构(46—50)

46、消息队列表 CREATE TABLE dingchengyu消息队列表 (id int(11) NOT NULL AUTO_INCREMENT COMMENT 序号,userId int(11) DEFAULT NULL COMMENT 用户id,msgTtype tinyint(4) DEFAULT 0 COMMENT 消息类型,createTime datetime DEFAULT NULL COMMENT 创建时间,sendTime datetim…

centos7安装真的Redmine-5.1.2+ruby-3.0.0

下载redmine-5.1.2.tar.gz&#xff0c;上传到/usr/local/目录下 cd /usr/local/ tar -zxf redmine-5.1.2.tar.gz cd redmine-5.1.2 cp config/database.yml.example config/database.yml 配置数据连接 #编辑配置文件 vi config/database.yml #修改后的内容如下 product…

学习CSS3,实现红色心形loading特效

试想一下&#xff0c;如果你的网站在加载过程中&#xff0c;loading图由一个老旧的菊花转动图片&#xff0c;变为一个红色的心形loading特效&#xff0c;那该有多炫酷啊。 目录 实现思路 初始化HTML部分 延迟动画是重点 设定动画效果 完整源代码 最后 实现思路 每个…

问界M7碰撞后车门打不开,都是隐形门把手的错?

问界M7碰撞后车门打不开&#xff0c;并不能简单地归咎于隐形门把手的设计。实际上&#xff0c;碰撞后车门无法打开可能涉及多个因素&#xff0c;具体分析如下&#xff1a; 隐藏式门把手的设计与工作原理&#xff1a;隐藏式门把手在正常状态下与车身表面齐平&#xff0c;解锁时才…

AI图书推荐:如何使用ChatGPT来提升逻辑分析能力

在一个日益由数据和技术驱动的世界中&#xff0c;进行逻辑思考和做出明智决策的能力比以往任何时候都更为关键。逻辑分析构成了理性思考的基础&#xff0c;引导我们穿越复杂问题&#xff0c;并帮助我们得出合理的结论。随着人工智能&#xff08;AI&#xff09;的出现&#xff0…

C语言-调试技巧

目录 一、调试介绍1.1 Debug和Release的介绍1.2 Windows环境调试介绍1.2.1 学会快捷键1.2.2 查看临时变量的值1.2.3 查看内存信息1.2.4 查看调用堆栈1.2.4 查看汇编信息1.2.5 查看寄存器信息 二、编程常见的错误2.1 编译型错误2.2 链接型错误2.3 运行时错误 三、易于调试的代码…

【C++】双指针算法:四数之和

1.题目 2.算法思路 这道题目十分困难&#xff0c;在leetcode上的通过率只有36%&#xff0c;大家要做好心理准备。 在做个题目前强烈建议大家先看看我的上一篇博客&#xff1a;有效三角形个数&#xff0c;看完之后再去leetcode上写一写三数之和&#xff0c;搞懂那两个题目之后…

网络安全实训Day16

网络空间安全实训-渗透测试 漏洞扫描 定义 扫描和探测目标范围内的主机存在哪些安全漏洞&#xff0c;或扫描目标范围内的那些主机存在某个指定的漏洞 漏扫工具 AWVS APPScan MSF 使用MSF扫描漏洞并利用 1.搜索需要的攻击模块 search ms17-010 2.使用攻击模块 use 模块名称…

jsp校园商城派送系统Myeclipse开发mysql数据库web结构java编程计算机网页项目

一、源码特点 JSP 校园商城派送系统 是一套完善的web设计系统&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统采用serlvetdaobean mvc 模式&#xff0c;系统主要采用B/S模式 开发。开发环境为TOMCAT7.0,Myeclipse8.…

每日一题-贪心算法

目录 前言 买入股票的最佳时机(1) 买入股票的最好时机(2) 前言 当你踏上贪心算法的旅程&#xff0c;仿佛置身于一场智慧的盛宴&#xff0c;每一步都是对问题解决方案的审慎选择&#xff0c;每一次决策都是对最优解的向往。贪心算法以其简洁高效的特性&#xff0c;被广泛运用于…

yolov5-pytorch-Ultralytics训练+预测+报错处理记录

一、前言 玩一段时间大模型&#xff0c;也该回归一下图像识别。本项目用于记录使用基于Ultralytics的yolov5进行目标检测测试。为什么用Ultralytics呢&#xff1f;答案有3 1、其良好的生态&#xff0c;方便我们部署到其它语言和设备上。因此本次测试结论&#xff1a;大坑没有&…

基于YOLOv8的水稻虫害识别系统,加入BiLevelRoutingAttention注意力进行创新优化

&#x1f4a1;&#x1f4a1;&#x1f4a1;本文摘要&#xff1a;基于YOLOv8的水稻虫害识别&#xff0c;阐述了整个数据制作和训练可视化过程&#xff0c;并加入BiLevelRoutingAttention注意力进行优化&#xff0c;最终mAP从原始的 0.697提升至0.732 博主简介 AI小怪兽&#xff…

pyqt标签常用qss格式设置

pyqt标签常用qss格式设置 QSS介绍标签常用的QSS设置效果代码 QSS介绍 Qt Style Sheets (QSS) 是 Qt 框架中用于定制应用程序界面样式的一种语言。它类似于网页开发中的 CSS&#xff08;Cascading Style Sheets&#xff09;&#xff0c;但专门为 Qt 应用程序设计。使用 QSS&…

[信息收集]-端口扫描--Nmap

端口号 端口号的概念属于计算机网络的传输层&#xff0c;标识这些不同的应用程序和服务而存在的。通过使用不同的端口号&#xff0c;传输层可以将接收到的数据包准确地传递给目标应用程序。 80&#xff1a;HTTP&#xff08;超文本传输协议&#xff09;用于Web浏览器访问网页 …

怎么证明E[E(X|Y,Z)Y]= E(X|Y)

性质8的证明 物理意义

中霖教育:资产评估师报考攻略

一、报考条件 1 参加资产评估师考试的基本条件:为中国公民 2 具有完全民事行为能力 3 具有高等院校专科以上(含专科)学历 符合上述报名条件&#xff0c;暂未取得学历(学位)的大学生可报名参加考试 二、报名时间 报名时间&#xff1a;2024年3月25日9:00至5月10日24:00 补…

《有限元分析及应用》《有限元分析基础教程》-曾攀-清华大学|pdf电子书+有限元分析及应用视频教程(全85讲) 曾攀、雷丽萍 ​​​+课件PPT

专栏导读 作者简介&#xff1a;工学博士&#xff0c;高级工程师&#xff0c;专注于工业软件算法研究本文已收录于专栏&#xff1a;《有限元编程从入门到精通》本专栏旨在提供 1.以案例的形式讲解各类有限元问题的程序实现&#xff0c;并提供所有案例完整源码&#xff1b;2.单元…

分割链表----一道题目的3种不同的解法

1.题目概述 以这个题目的事例作为例子&#xff0c;我们看一下这个题目到底是什么意思&#xff08;Leedcode好多小伙伴说看不懂题目是什么意思&#xff09;&#xff0c;就是比如一个x3&#xff0c;经过我们的程序执行之后&#xff1b;大于3的在这个链表的后面&#xff0c;小于3的…

【资源分享】CAD Map 3D2024安装教程

::: block-1 “时问桫椤”是一个致力于为本科生到研究生教育阶段提供帮助的不太正式的公众号。我们旨在在大家感到困惑、痛苦或面临困难时伸出援手。通过总结广大研究生的经验&#xff0c;帮助大家尽早适应研究生生活&#xff0c;尽快了解科研的本质。祝一切顺利&#xff01;—…