手把手实现AVL——二叉平衡搜索树

概述:本文介绍AVL树的实现,从零构建一颗AVL树,以及对应的插入、删除、旋转操作

什么是AVL树?

AVL树是带有平衡条件的二叉查找树,二叉查找树又区别于二叉树:保证有序

这个平衡条件是每个节点的左右子树高度差不超过 1

一、AVL树节点设计:

typedef struct node {int key;     // 节点存储的数据int height;  // 节点在树中的高度struct node *parent; // 指向其父节点的指针struct node *left;   // 指向其左子节点struct node *right;  // 指向其右子节点
} node;

这就是构建一颗AVL树的最小单位:节点

其中key是node可以存储的数据,height则用来保存节点的高度信息,其余的三个指针用来构建AVL树

此外,为了提高效率,我们维护一个结构体对象,用来指示整棵树的基本信息

struct tree{node *root; // 整棵树的根节点 node *min;  // 整棵搜索树中的最小节点node *max;  // 整颗搜索树中的最大节点
};

需要注意的是,每对AVL树做一次修改,都有可能改变这些指针,因此在后续的insert、delete等操作的设计时,需要考虑到这一点

二、初始化和辅助函数

对于每一个节点,它的声明周期从分配空间并初始化开始:

// 创建新节点
node* create_node(int k) {// 申请空间node* node = (struct node *)malloc(sizeof(struct node));if (!node) return NULL;node->key = k;node->parent = NULL; node->left = NULL;node->right = NULL;node->height = 1; // 默认最小高度为 1return node; 
}

可以看到,创建一个节点只需要指定其key值,因为在创建之初,只有这一个属性是确定的

三个指针都应该被初始化为NULL,避免内存访问错误。

获取节点的高度:

// 节点高度
int height(node *n) { // 获取当前heightreturn n ? n->height : 0;
}// 更新节点高度
int update_height(node *n) { // 递归,一个节点的高度是其左右子节点的高度中最大值 + 1if (n != NULL) { n->height = MAX(height(n->left), height(n->right)) + 1;}return n->height;
}

找到最小节点、最大节点、根节点

node * find_min(node *root) {if (root == NULL) return root;node *cur = root;while (!(cur->left == NULL && cur->right == NULL)) { // 最小节点在左子树上cur = cur->left;}return cur;
}node * find_max(node *root) {if (root == NULL) return root;node *cur = root;while (!(cur->left == NULL && cur->right == NULL)) { // 最大节点在右子树上cur = cur->right;}return cur;
}node * find_root(node *n) {node *cur = n;while (cur->parent != NULL) {cur = cur->parent;}return cur;
}

其中根节点的查找很简单,给定任意一个节点就能找到整棵树的根节点

计算一个节点的左右子树高度差(用于判断平衡条件)

int get_balance(node *n) {return n ? update_height(n->left) - update_height(n->right) : 0;
}

高度差的计算默认使用 左子树高度 - 右子树高度

根据key查找一个节点:

node * search_node(int key) {node * temp = tree->root;while (!(temp->left == NULL && temp->right == NULL)) {if (key < temp->key)temp = temp->left;else if (key > temp->key)temp = temp->right;else return temp;}return NULL;
}

三、插入、删除、旋转

1.插入一个节点

插入一个节点有以下几种情况:

a. 根节点为空,则待插入节点为根节点

b. 根节点不为空,则待插入节点将作为叶子节点
在这里插入图片描述

void insert(node *root, node *node) {if (node == NULL) return;// 根节点为空的情况if (root == NULL) {root = node;tree->root = node; // 更新全局 roottree->min = node;tree->max = node;return;}// 根节点不为空struct node *cur = root;int k = node->key;// 遍历找到待插入节点的父节点while (!(cur->left == NULL && cur->right == NULL)) {if (k <= cur->key) {cur = cur->left;} else {cur = cur->right;}}// 插入到父节点下if (k <= cur->key) {cur->left = node;node->parent = cur;} else {cur->right = node;node->parent = cur;}insert_fix(node);tree->min = find_min(tree->root);tree->max = find_max(tree->root);
}

其中的insert_fix()函数是用来修正插入后造成的树不平衡问题,接下来会介绍

2.删除一个节点

删除一个节点比较复杂,可能有以下几种情况:

1.待删除节点是叶子节点,直接删除

在这里插入图片描述

2.待删除节点有一个子节点

在这里插入图片描述

3.待删除节点有左右两个子节点

在这里插入图片描述

注意第三种情况:

使用待删除节点的右子树的最小值或左子树的最大值来替换的原因是,右子树的最小节点一定是叶子节点,且大于待删除节点的右子节点,这个叶子节点在被替换到待删除节点的位置后,它的删除操作很简单

node* delete(node *root, node* n) {if (n == NULL) return NULL;node *temp;if (root == NULL) printf("target is not found\n");else { if (n->key < root->key)root->left = delete(root->left, n); // 在左子树递归查找删除else if (n->key > root->key)root->right = delete(root->right, n); // 在右子树递归查找删除else { // 删除当前根节点if (root->left && root->right) { // 被删除节点有左右两个子节点temp = find_min(root->right); // 用右子树的最小节点替换被删除节点root->key = temp->key; // 替换被删除节点root->right = delete(root->right, temp); // 删除用作替换的节点}else { // 被删除节点只有一个或无子节点temp = root;if (root->left == NULL) root = root->right;elseroot = root->left;free (temp); // 被删除的节点}}}    update_height(root);delete_fix(find_min(tree->root));delete_fix(find_max(tree->root));tree->root = find_root(tree->root);update_height(tree->root);tree->min = find_min(tree->root);tree->max = find_max(tree->root);return root; // 删除后,返回根节点
}
左右单旋转、左右双旋转

1.右单旋

在这里插入图片描述

2.左单旋

在这里插入图片描述

3.左右双旋
在这里插入图片描述

4.右左双旋

在这里插入图片描述

先介绍右旋的实现

void right_rotate(node *up) {node *root = up->parent;node *down = up->right;if (down->left) {          // 右旋时处理子节点的左子节点up->right = down->left;down->left->parent = up;}// 调换上下位置down->left = up;     down->parent = root;up->parent = down;// heightup->height = update_height(up);down->height = update_height(down); 
}

左旋和右旋的区别就在于,把上述代码的left和right字段调换位置

而左右和右左双旋就是进行两次旋转

因此,所有的旋转操作,只看这一段代码就够了,根据图示很好理解

修正代码:

void insert_fix(node *n) {while (n != tree->root) {update_height(n);int balance = get_balance(n);if (balance > 1) {if (get_balance(n->left) < 0) { // 考虑需要左右双旋的情况left_rotate(n->left);}right_rotate(n);} else if (balance < -1) {if (get_balance(n->right) > 0) { // 考虑右左双旋right_rotate(n->right);}left_rotate(n);}n = n->parent;}
}   

delete和insert的修正代码相同

修正逻辑如下:

1.从叶子节点开始判断平衡条件

2.对于不平衡的节点,根据get_balance的值判断需要左旋还是右旋

3.判断不平衡节点的子节点是否平衡,因为上一次旋转操作可能会造成其兄弟节点不平衡

推荐学习 https://xxetb.xetslk.com/s/p5Ibb

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

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

相关文章

[数据集][目标检测]红外人狗检测数据集VOC+YOLO格式185张2类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;185 标注数量(xml文件个数)&#xff1a;185 标注数量(txt文件个数)&#xff1a;185 标注类别…

Python数据分析常用函数

Python基础 数字处理函数 Python提供了用于数字处理的内置函数和内置模块(math)&#xff0c;使用内置模块&#xff0c;需要先导入 import math。 内置函数math模块abs(-5)返回绝对值math.ceil(2.3)返回不小于x的最小整数divmod(9,4)返回商和余数math.floor(2.3)返回不大于x的…

GLCM 特征和LBP特征提取

GLCM 特征 GLCM&#xff08;灰度共生矩阵&#xff09;特征用于描述图像中像素灰度级之间的空间关系&#xff0c;常用于纹理分析。GLCM特征通过统计图像中各个灰度级对之间的出现频率来描述图像的纹理特征。GLCM特征包括能量&#xff08;ASM&#xff09;、对比度&#xff08;Co…

对于创建相关项目时,项目出现红色感叹号,且无jre环境显示,应该怎么解决?

首先&#xff0c;假设你已经下载好了相关你的jre环境&#xff0c;注意&#xff1a;如果你的jre不想用之前用的默认的话&#xff0c;你应该新建一个新的文件路径来存储你的新的jre环境下的项目文件。 先直接new->project->javaproject 点击next: 显示如下&#xff1a;&…

JavaScript表达式语句二

异常处理语句 异常标识一种非中正常得信息&#xff0c;它提示程序在运行过程中发生了意外或错误&#xff0c;然后JavaScript通过一定的方式把它暴露出来&#xff0c;这叫做抛出异常。抛出异常操作表示系统告诉我们当前程序出现了问题&#xff0c;JavaScript使用异常处理语句来…

以一道简单的例题计算灵敏性分析

在例1.1中&#xff0c;全部的变量包括&#xff1a;猪的重量w(磅),从现在到出售猪期间经历的时间t(天),t天内饲养猪的花费C(美元),猪的市场价格p(美元/磅),售出生猪所获得的收益R(美元),我们最终获得的净收益P(美元).这里还有一些其他的有关量&#xff0c;如猪的初始重量(200磅)…

Python 全栈体系【四阶】(五十四)

第五章 深度学习 十二、光学字符识别&#xff08;OCR&#xff09; 3. 文字识别技术 3.1 CRNNCTC(2015) CRNN&#xff08;Convolutional Recurrent Neural Network&#xff09;即卷积递归神经网络&#xff0c;是DCNN和RNN的组合&#xff0c;专门用于识别图像中的序列式对象。…

掌握常用的域信息收集的方法和域控制器攻击的方法(渗透课程)

域信息收集 【实验目的】 通过利用PsExec命令远程连接内网主机&#xff0c;分别收集查询域用户信息和域控制器相关信息&#xff0c;了解并掌握如何收集域内的信息命令。 【知识点】 域信息收集 【实验原理】 NET命令是功能强大的以命令行方式执行的工具。它包含了管理网络…

Nginx企业级负载均衡:技术详解系列(11)—— 实战一机多站部署技巧

你好&#xff0c;我是赵兴晨&#xff0c;97年文科程序员。 工作中你是否遇到过这种情况&#xff1a;公司业务拓展&#xff0c;新增一个域名&#xff0c;但服务器资源有限&#xff0c;只能跟原有的网站共用同一台Nginx服务器。 也就是说两个网站的域名都指向同一台Nginx服务器…

过滤器Filter

目录 概述 Filter快速入门 概述 概念&#xff1a;Filter过滤器&#xff0c;是JavaWeb三大组件&#xff08;Servlet,Filter,Listener&#xff09;之一。 过滤器可以把对资源的请求拦截下来&#xff0c;从而实一些特殊的功能。 过滤器一般完成一些通用的操作&#xff0c;比如…

MySQL(二)基本SQL语句以及基本函数应用

1、基本SQL语句 MySQL中定义数据字段的类型对你数据库的优化是非常重要的。 MySQL支持多种类型&#xff0c;大致可以分为三类&#xff1a;数值、日期/时间和字符串&#xff08;字符&#xff09;类型。 - 函数应用在sql语句中 -- 临时表 select now() from dual;-- 数…

活动预告|与 Zilliz 共探亚马逊云科技中国峰会

亚马逊云科技中国峰会是由全球云计算的开创者和引领者亚马逊云科技举办的一年一度的科技盛会。 参与者将有机会了解云计算推动行业发展的新趋势与解决方案、生成式 AI 等前沿技术的落地实践&#xff0c;通过大量成功案例解析&#xff0c;获得灵感及经验来解决实际问题&#xff…

民国漫画杂志《时代漫画》第26期.PDF

时代漫画26.PDF: https://url03.ctfile.com/f/1779803-1248635183-9832d2?p9586 (访问密码: 9586) 《时代漫画》的杂志在1934年诞生了&#xff0c;截止1937年6月战争来临被迫停刊共发行了39期。 ps: 资源来源网络!

AGV与智能仓储的应用案例

背景介绍 该企业的智能工厂专注于高端家用电器的生产与研发&#xff0c;包括电子坐便盖、电子坐便器、吸尘器、洗碗机等&#xff0c;覆盖8条关键产线。面对日益增长的市场需求和生产节奏的加快&#xff0c;传统的物流方式已无法满足高效、精准的生产要求。为此&#xff0c;企业…

电流采样(分流器与霍尔传感器)

在对于电信号采集的设计中&#xff0c;其中对电流信号的采集是非常常见的&#xff0c;根据电流信号的属性&#xff0c;如信号大小、信号周期等因素&#xff0c;以及采样的需求指标不一样&#xff0c;往往需要选择不同的采样方式进行采样。 下面主要介绍分流器和霍尔元件采样电…

【贪心算法指针】C++ 解决子数组 / 子序列的相关问题(最大数、数组和减半的最小操作数、连续/递增序列)

文章目录 1. 前言1.1 贪心算法介绍 2. 算法题2.1_将数组和减半的最少操作次数2.2_最大数2.3_最长递增子序列2.4_递增的三元子序列2.5_最长连续递增序列2.6_数组中的最长连续子序列2.7_在字符串中找出连续最长的数字串 1. 前言 1.1 贪心算法介绍 贪心算法&#xff08;Greedy A…

一文搞透常见的Python编码陷阱(上)(分析+案例)

一个认为一切根源都是“自己不够强”的INTJ 个人主页:用哲学编程-CSDN博客专栏:每日一题——举一反三Python编程学习Python内置函数 Python-3.12.0文档解读 目录 一、别忘了冒号 1. if 语句 2. while 语句 3. for 语句 4. 函数定义 5. 类定义 6. try/except 语句 …

RTDETR结合CVPR2024最新图像增强算法!让你的模型无惧风雨【含端到端推理脚本】

如何有效地探索雨痕的多尺度表示对于图像去雨是很重要的。与现有的基于Transformer的方法相比,这些方法主要依赖于单一尺度的雨痕外观,我们开发了一个端到端的多尺度Transformer,利用各种尺度中潜在有用的特征来促进高质量的图像重建。为了更好地探索空间变化的雨痕的常见退…

满帮集团 Eureka 和 ZooKeeper 的上云实践

作者&#xff1a;胡安祥 满帮集团&#xff0c;作为“互联网物流”的平台型企业&#xff0c;一端承接托运人运货需求&#xff0c;另一端对接货车司机&#xff0c;提升货运物流效率。2021 年美股上市&#xff0c;成为数字货运平台上市第一股。根据公司年报&#xff0c;2021 年&a…

网络协议——FTP(简介、搭建FTP服务端)

一、简介 1、什么是FTP&#xff1f; FTP&#xff08;File Transfer Protocol&#xff0c;文件传输协议&#xff09; TCP/IP 协议组的协议之一。常用20&#xff08;数据&#xff09;、21&#xff08;命令&#xff09;端口作为通讯端口。&#xff08;22为SSH端口&#xff09;F…