平衡二叉树(AVL) 的认识与实现

文章目录

  • 1 基本
    • 1.1 概念
    • 1.2 特点
    • 1.3 构建
    • 1.4 调整
      • 1.4.1 RR
        • 1.4.1.1 示例
        • 1.4.1.2 多棵树不平衡
      • 1.4.2 LL
        • 1.4.2.1 示例
      • 1.4.3 LR
        • 1.4.3.1 示例
      • 1.4.4 RL
        • 1.4.4.1 示例
    • 1.5 实现
      • 1.5.1 示例
      • 1.5.2 完善

1 基本

1.1 概念

平衡二叉树是一棵合理的二叉排序树

解释

对于这么一个序列

在这里插入图片描述

如果按照普通的排序思路,5>4>3>2>1,会得到这么一棵树

如果想要查询结点5,则需要比对很多次;

而如果以3为根,则可以得到一棵较为合理的树

这样就搜索结点5只需要3次即可

为了实现较为合理的排序,AVL就因此诞生

1.2 特点

平衡二叉树有个至关重要的特点:

左右子树高度差不超过1

由此减少树的深度,从而减少查找次数

1.3 构建

  • 本质上与构建二叉排序树一致
  • 在构建二叉排序树的过程中,如果发现树不符合左右子树高度差不超过1的特点,需要进行调整

1.4 调整

通过**失衡树的根节点root导致树失衡的结点node**来调整新的树

而根据2个方向来选择调整方法

  • 导致树失衡的结点noderoot的哪一侧
  • noderoot孩子结点的哪一侧

调整方法

  • LL:上面两个方向均在左侧
  • RR:上面两个方向均在右侧
  • RL:上面两个方向一右一左
  • LR:上面两个方向一左一右

1.4.1 RR

RR

  • 取中间节点,使其父节点成为其左子树
  • 如果它有左子树,则先使其父节点成为其左子树,然后使其原有的左子树连接到现有左子树的右子树上
//	代码的实现则是反过来,先连接原有左子树到现有左子树的右子树,再连接到中间节点的右子树
root 1	node/child 3
root->right_child = child->left_child;
child->left_child = root;
1.4.1.1 示例

对于开头提到的序列

在这里插入图片描述

进行正常排序

可见,root1node3

  • noderoot的右侧:R
  • noderoot孩子节点的右侧:R

两个都是R

进行调整

在这里插入图片描述

如果2原先有左子树,调整:

在这里插入图片描述

1.4.1.2 多棵树不平衡

注意:如果遇到多棵树不平衡,则调整最小树

将上面的序列继续排序

在这里插入图片描述

排序

在这里插入图片描述

如图,有两棵树失衡,一棵树最大的树12345,一颗是345,我们只用调整最小的345即可

这棵也是RR,直接调整

在这里插入图片描述

1.4.2 LL

RR完全相反

LL

  • 取中间节点,使其父节点成为其右子树
  • 如果它有右子树,则先使其父节点成为其右子树,然后使其原有的右子树连接到现有右子树的左子树上
//	代码的实现则是反过来,先连接原有左子树到现有左子树的右子树,再连接到中间节点的右子树
root 5	node/child 3
root->left_child = child->right_child;
child->right_child = root;
1.4.2.1 示例

再来一组数据

在这里插入图片描述

正常排序

进行LL排序

在这里插入图片描述

继续

继续排序最小树

在这里插入图片描述

1.4.3 LR

LR:

  • 取最后一个节点,使其作为父节点
  • 将其原有的父节点作为它的左子树,将它原有的父节点的父节点作为它的右子树
  • 如果它有左子树,则先使其父节点成为其左子树,然后使其原有的左子树连接到现有左子树的右子树上
  • 如果它有右子树,则先将它原有的父节点的父节点作为它的右子树,然后将其原有的右子树连接到它原有的父节点的父节点的左子树上

总结

  • RR
  • LL
//	代码的实现则是反过来
root 7	node/child 6
root->left_child = child->right_child;	child-right_>child = root;		//有右子树
root->left_child->right_child = child->left_child;	root->left_child->right_child = child->left_child 	//有左子树
child->right_child = root;
root = child;
1.4.3.1 示例

在这里插入图片描述

进行正常排序

在这里插入图片描述

两棵树失衡

在这里插入图片描述

调整最下面的树

可见,root7node6

  • noderoot的左侧:L
  • noderoot孩子节点的右侧:R

LR

进行调整

在这里插入图片描述

1.4.4 RL

RL:

  • 取最后一个节点,使其作为父节点
  • 将其原有的父节点作为它的右子树,将它原有的父节点的父节点作为它的左子树
  • 如果它有左子树,则先将它原有的父节点的父节点作为它的左子树,然后将其原有的左子树连接到它原有的父节点的父节点的右子树上
  • 如果它有右子树,则先使其父节点成为其右子树,然后使其原有的右子树连接到现有右子树的左子树上,

总结

  • LL
  • RR
//	代码的实现则是反过来
root 1	node/child 6
root->right_child->left_child = child->right_child;	child-right_>child = root->right_child;		//有右子树
root->right_child = child->left_child;	child->left_child = root;		//有左子树
child->left_child = root;
root = child;
1.4.4.1 示例

在这里插入图片描述

进行正常排序

在这里插入图片描述

已经失衡,进行调整

可见,root1node6

  • noderoot的左侧:R
  • noderoot孩子节点的右侧:L

RL

进行调整

在这里插入图片描述

然后继续插入7/10

在这里插入图片描述

1.5 实现

  • 建立平衡二叉树的过程就是建立一棵二叉搜索树的过程
  • 而在建立过程中我们需要对树进行调整,调整需要用到树的高度,因此我们节点的结构体中需要添加一个height字段来标记当前树的高度

1.5.1 示例

只实现二叉排序树,还未使用字段height

#include<iostream>
using namespace std;
//节点
typedef struct TreeNode {int data;int height;struct TreeNode* left_child;struct TreeNode* right_child;
}TreeNode;
//进行二叉排序树的构建
void AVLInsert(TreeNode** T, int data)
{if (*T == NULL)	//没有节点{*T = (TreeNode*)malloc(sizeof(TreeNode));	//创建一个(*T)->data = data;(*T)->height = 0;	//高度设为0(*T)->left_child = NULL;(*T)->right_child = NULL;}else if (data < (*T)->data){AVLInsert(&(*T)->left_child, data);	//比根节点的data大,插入在左子树}else if (data > (*T)->data){AVLInsert(&(*T)->right_child, data);}
}
//前序遍历打印树
void PreOrder(TreeNode* T)
{if (T){cout << T->data;PreOrder(T->left_child);	//递归调用左子树PreOrder(T->right_child);	//递归调用右子树}
}
int main()
{TreeNode* T = NULL;int nums[5] = { 1,2,3,4,5 };for (int i = 0; i < 5; i++){	//构建树AVLInsert(&T, nums[i]);}PreOrder(T);cout << endl;return 0;
}

在这里插入图片描述

1.5.2 完善

  • 获取高度(其实我们结构体中有定义height字段可以直接调用,但为了易读我这里创建一个函数得到height
//获取高度高度
int GetHeight(TreeNode* node)
{return node ? node->height : 0;	//节点不为空,则返回节点的height值,否则返回0
}
  • 获取最大高度

    //获取最大高度
    int GetMax(int a, int b)
    {return a > b ? a : b;
    }
    
  • 实现RRLL

//RR旋转
void rrRotation(TreeNode* root, TreeNode** node)
{TreeNode* tmp = root->right_child;root->right_child = tmp->left_child;	//原有左子树连接到现有左子树的右子树上tmp->left_child = root;					//使其父节点成为其现有的左子树root->height = GetMax(GetHeight(root->left_child), GetHeight(root->right_child)) + 1;tmp->height = GetMax(GetHeight(tmp->left_child), GetHeight(tmp->right_child)) + 1;*node = tmp;
}//LL旋转
void llRotation(TreeNode* root, TreeNode** node)
{TreeNode* tmp = root->left_child;root->left_child = tmp->right_child;	//原有右子树连接到现有右子树的左子树上tmp->right_child = root;				//使其父节点成为其现有的右子树root->height = GetMax(GetHeight(root->left_child), GetHeight(root->right_child)) + 1;tmp->height = GetMax(GetHeight(tmp->left_child), GetHeight(tmp->right_child)) + 1;*node = tmp;
}
  • 实现LRRL,就是调用LLRR

    //LR
    void lrTotation(TreeNode** T)
    {rrRotation((*T)->left_child, &(*T)->left_child);llRotation(*T, T);
    }//RL
    void rlTotation(TreeNode** T)
    {llRotation((*T)->right_child, &(*T)->right_child);rrRotation(*T, T);
    }
    
  • 最后完善构建二叉树

    //进行二叉排序树的构建
    void AVLInsert(TreeNode** T, int data)
    {if (*T == NULL)	//没有节点{*T = (TreeNode*)malloc(sizeof(TreeNode));	//创建一个(*T)->data = data;(*T)->height = 0;	//高度设为0(*T)->left_child = NULL;(*T)->right_child = NULL;}else if (data < (*T)->data)		//比根节点的data小,插入在左子树	:L{AVLInsert(&(*T)->left_child, data);//获取当前节点左右子树的高度int left_height = GetHeight((*T)->left_child);int right_height = GetHeight((*T)->right_child);//判断高度差if (left_height - right_height > 1){if (data < (*T)->left_child->data)//小于父节点左子树的值,说明在左边	:L{//LLllRotation(*T, T);}else// : R{//LRlrTotation(T);}}}else if (data > (*T)->data)//比根节点的data大,插入在右子树			:R{AVLInsert(&(*T)->right_child, data);						//获取当前节点左右子树的高度int left_height = GetHeight((*T)->left_child);int right_height = GetHeight((*T)->right_child);//判断高度差if (right_height - left_height > 1){if (data > (*T)->right_child->data)	//大于父节点右子树的值,说明在右边	:R{//RRrrRotation(*T, T);}else//:L{//RLrlTotation(T);}}}(*T)->height = GetMax(GetHeight((*T)->left_child), GetHeight((*T)->right_child)) + 1;
    }
    
  • main

    int main()
    {TreeNode* T = NULL;int nums1[5] = { 1,2,3,4,5 };int nums2[5] = { 5,4,3,2,1 };int nums3[5] = { 8,7,9,5,6 };int nums4[5] = { 1,8,6,7,10 };for (int i = 0; i < 5; i++){	//构建树AVLInsert(&T, nums4[i]);}PreOrder(T);cout << endl;return 0;
    }
    

    在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Ceph Block Device】块设备挂载使用

文章目录 前言创建pool创建user创建image列出image检索image信息调整image大小增加image大小减少image大小 删除image从pool中删除image从pool中“延迟删除”image从pool中移除“延迟删除的image” 恢复image恢复指定pool中延迟删除的image恢复并重命名image 映射块设备格式化i…

C++DAY44

#include <iostream>using namespace std;class Animal//封装 动物 基类 { private:string name; public:Animal() {}Animal(string n):name(n){}virtual void perform() //虚函数{cout << "欢迎来到动物园" << endl;} };class Lion:public Animal…

c/c++--字节对齐(byte alignment)

1. 默认字节对齐 在所有结构体成员的字节长度都没有超出操作系统基本字节单位(32位操作系统是4,64位操作系统是8)的情况下 按照结构体中字节最大的变量长度来对齐&#xff1b;若结构体中某个变量字节超出操作系统基本字节单位 那么就按照系统字节单位来对齐。 注意&#xff1…

视频监控系统/安防视频平台EasyCVR广场视频细节优化

安防视频监控系统/视频云存储/安防监控EasyCVR视频汇聚平台基于云边端智能协同&#xff0c;支持海量视频的轻量化接入与汇聚、转码与处理、全网智能分发、视频集中存储等。安防视频汇聚平台EasyCVR拓展性强&#xff0c;视频能力丰富&#xff0c;可实现视频监控直播、视频轮播、…

【Python 零基础入门 】安装 环境配置

【Python 零基础入门 】第一课 安装 & 环境配置 Python 零基础入门 第一课 安装 & 环境配置Python 的历史Python 的前景安装了解你的操作系统Python 安装环境配置 PyCharm 安装第一个程序 Python 零基础入门 第一课 安装 & 环境配置 在当今的技术时代, 编程语言正…

微信小程序/vue3/uview-plus form兜底校验

效果图 代码 <template><u-form :model"form" ref"formRole" :rules"rules"><u-form-item prop"nickname"><u-input v-model"form.nickname" placeholder"姓名" border"none" /&…

没用的知识增加了,尝试用文心实现褒义词贬义词快速分类

尝试用文心实现褒义词贬义词快速分类 一、我的需求二、项目环境搭建千帆SDK安装及使用流程 三、项目实现过程创建应用获取签名调用接口计算向量积总结 百度世界大会将于10月17日在北京首钢园举办&#xff0c;今天进入倒计时五天了。通过官方渠道的信息了解到&#xff0c;这次是…

Web后端开发登录校验及JWT令牌,过滤器,拦截器详解

如果用户名正确则成功进入 登录功能 代码 Controller Service Mapper 结果 若登录成功结果如下: 如果登录失败,结果如下 登录校验 为什么需要登录校验 有时再未登录情况下, 我们也可以直接访问部门管理, 员工管理等功能 因此我们需要一个登录校验操作, 只有确认用户登录…

【Debian】报错:su: Authentication failure

项目场景&#xff1a; 今天我重新刷了一个debian系统。 系统版本&#xff1a; # 查看系统版本 lsb_release -a 我的系统版本&#xff1a; No LSB modules are available. Distributor ID&#xff1a;Debian Description: Debian GNU/Linux 12 &#xff08;bookworm&#xff…

优雅而高效的JavaScript——箭头函数

&#x1f917;博主&#xff1a;小猫娃来啦 &#x1f917;文章核心&#xff1a;优雅而高效的JavaScript——箭头函数 文章目录 前言箭头函数的基本语法和特点箭头函数的语法箭头函数的词法绑定特性箭头函数的this值箭头函数无法使用arguments对象 箭头函数与传统函数的比较箭头函…

每年高考时间是几月几号 高考开始时间

高考是高中生最重要的一个阶段&#xff0c;甚至影响着很多学生的未来&#xff0c;相信大家都很关注高考的具体时间是什么时候&#xff0c;本次将详细给您介绍高考的具体开始时间以及结束时间。 每年高考的时间都是6月7日开始&#xff0c;一共持续三天时间左右&#xff0c;但是…

身份证号码,格式校验:@IdCard(自定义注解)

目标 自定义一个用于校验 身份证号码 格式的注解IdCard&#xff0c;能够和现有的 Validation 兼容&#xff0c;使用方式和其他校验注解保持一致&#xff08;使用 Valid 注解接口参数&#xff09;。 校验逻辑 有效格式 符合国家标准。 公民身份号码按照GB11643&#xff0d;…

竞赛选题 深度学习+opencv+python实现车道线检测 - 自动驾驶

文章目录 0 前言1 课题背景2 实现效果3 卷积神经网络3.1卷积层3.2 池化层3.3 激活函数&#xff1a;3.4 全连接层3.5 使用tensorflow中keras模块实现卷积神经网络 4 YOLOV56 数据集处理7 模型训练8 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &am…

印度网络安全:威胁与应对

随着今年过半&#xff0c;我们需要评估并了解不断崛起的网络威胁复杂性&#xff0c;这些威胁正在改变我们的数字景观。 从破坏性的网络钓鱼攻击到利用人工智能的威胁&#xff0c;印度的网络犯罪正在升级。然而&#xff0c;在高调的数据泄露事件风暴中&#xff0c;我们看到了政…

游戏反虚拟机检测方案

近年来&#xff0c;游戏市场高速发展&#xff0c;随之而来的还有图谋利益的游戏黑产。在利益吸引下&#xff0c;游戏黑产扩张迅猛&#xff0c;攻击趋势呈现出角度多样化的特点。 在这一趋势下&#xff0c;游戏安全防护的检测覆盖率显得尤为重要。如果游戏在某一环节出现被绕过…

Linux系统卡顿处理记录(Debian)

问题现象描述 现象linux操作系统卡顿&#xff08;就是很慢&#xff09;&#xff0c;但是系统任然能够使用。 文章一步步的排查并且定位问题。 排查步骤 1. 使用top命令查看CPU是否占用过高。&#xff08;未发现&#xff09;排除问题 2. 使用df -h查看硬盘是否被占满。&#…

浏览器唤起钉钉 各项功能

浏览器唤起钉钉对应人员聊天 文档地址 https://open.dingtalk.com/document/client/unified-routing-protocol 唤起聊天 不过只能唤起叮叮的名片 id为叮叮号 <a href"dingtalk://dingtalkclient/action/sendmsg?dingtalk_id{id}"></a>id&#xff1a; …

Spark 9:Spark 新特性

Spark 3.0 新特性 Adaptive Query Execution 自适应查询(SparkSQL) 由于缺乏或者不准确的数据统计信息(元数据)和对成本的错误估算(执行计划调度)导致生成的初始执行计划不理想&#xff0c;在Spark3.x版本提供Adaptive Query Execution自适应查询技术&#xff0c;通过在”运行…

vite+vue3+ts中使用require.context | 报错require is not defined | 获取文件夹中的文件名

vitevue3ts中使用require.context|报错require is not defined|获取文件夹中的文件名 目录 vitevue3ts中使用require.context|报错require is not defined|获取文件夹中的文件名一、问题背景二、报错原因三、解决方法 一、问题背景 如题在vitevue3ts中使用required.context时报…

《UnityShader入门精要》学习1

读者可以在开源网站github&#xff08;https://github.com/candycat1992/Unity_Shaders_Book&#xff09;上下载本书的源代码。 第二章 渲染流水线 渲染流水线的最终目的在于生成或者说是渲染一张二维纹理&#xff0c;即我们在电脑屏幕上看到的所有效果&#xff0c;它的输入是…