手撕AVL树(map和set底层结构)(1)

troop主页
今日鸡汤:Action may out always bring happiness;but there is no happiness without action.
行动不一定能带来快乐,但不行动一定不行
C++之路还很长

手撕AVL树

  • 一 AVL树概念
  • 二 模拟实现AVL树
    • 2.1 AVL节点的定义
  • 三 插入
    • 更新平衡因子(重点)
  • 四 旋转
    • 1.左单旋
    • 1.1 左单旋完整代码
    • 2 右单旋
    • 2.2 右单旋完整代码
    • 3 双旋一(左+右)
    • 3.2左右双旋完整代码
    • 4 双旋二(右+左)
    • 4.2 右左双旋完整代码
  • 旋转总结
  • 五 验证AVL树的正确性

一 AVL树概念

二叉搜索树虽然可以缩短查找的效率,但是当数接近有序或者二叉搜索数接近单支树,查找的效率就相当于在顺序表中查找。所以为了解决这种极端环境,,两位俄罗斯的数学家G.M.Adelson-Velskii
和E.M.Landis在1962年发明了一种解决上述问题的方法:
当向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之差的绝对值不超过1(需要对树中的结点进行调整),即可降低树的高度,从而减少平均搜索长度。
AVL树的性质

  1. 他的左右子树都是AVL树
  2. 左右子树的高度差(平衡因子)的绝对值不超过1 在这里插入图片描述
    注(以下代码中,平衡因子=|右子树高度-左子树高度|

二 模拟实现AVL树

2.1 AVL节点的定义

template<class K, class V>
struct AVLTreeNode
{AVLTreeNode<K, V>* _left;AVLTreeNode<K, V>* _right;AVLTreeNode<K, V>* _parent;int _bf; // balance factorpair<K, V> _kv;AVLTreeNode(const pair<K, V>& kv):_left(nullptr), _right(nullptr), _parent(nullptr), _bf(0), _kv(kv){}
};

三 插入

我们这里AVL只写插入,插入就可以让我们很好的了解AVL的底层实现了。
AVL树也是二叉搜索树,只是在此基础上增加了平衡因子的调整。所以我们的插入就分成了两部分。

  1. 按照二叉搜索树的规则插入
  2. 更新平衡因子
	bool Insert(const pair<K, V>& kv){if (_root == nullptr){_root = new Node(kv);return true;}Node* parent = nullptr;Node* cur = _root;while (cur){if (cur->_kv.first < kv.first){parent = cur;cur = cur->_right;}else if (cur->_kv.first > kv.first){parent = cur;cur = cur->_left;}else{return false;}}cur = new Node(kv);if (parent->_kv.first < kv.first){parent->_right = cur;}else{parent->_left = cur;}cur->_parent = parent;

这个就是我们前面已经说过的二叉搜索树的插入规则。现在我们重点看如何更新平衡因子。

更新平衡因子(重点)

平衡因子更新原则:

  • cur插入在parent的左边 平衡因子减减
  • cur插入在parent的右边 平衡因子加加

思考:插入节点后会影响哪些节点的平衡因子?
会影响新插入节点的部分祖先。
是否影响爷爷节点取决于parent的高度是否有变化
首先父亲节点一定会被影响,其次重点考虑的应该是爷爷节点所受的影响。这里比较抽象我们需要借助图像来把每一种可能写出来。
第一种更新后p->_bf0
在这里插入图片描述
这种就是更新之前p的高度为1or-1,新节点插入在了比较矮的那一端,左右平衡。
第二种更新后p->_bf
1or-1
在这里插入图片描述

更新之前,p的高度平衡,cur插入在一侧,p不平衡了,这里p的高度变化爷爷节点也受到了影响,就需要向上更新。
第三种更新后p->_bf==2or-2
在这里插入图片描述
违反了AVL树的规则要进行旋转。
我们现在总结一下,什么情况下更新就结束了。

1.插入后父亲的平衡因子为0,更新结束
2.向上更新到,cur=root的位置时,更新结束
3.违反规则需要旋转,旋转之后,更新结束

		//调整平衡因子while (parent){if (cur == parent->_left){parent->_bf--;}else{parent->_bf++;}//情况一if (parent->_bf == 0){break;}//情况二else if (parent->_bf == 1 || parent->_bf == -1){cur = cur->_parent;parent = parent->_parent;}//情况三else if (parent->_bf == 2 || parent->_bf == -2){// 旋转处理if (parent->_bf == 2 && cur->_bf == 1){RotateL(parent);}else if (parent->_bf == -2 && cur->_bf == -1){RotateR(parent);}else if (parent->_bf == -2 && cur->_bf == 1){RotateLR(parent);}else{RotateRL(parent);}break;}//插入树之前这个树就不符合AVL树else{// 插入之前AVL树就有问题assert(false);}

这部分代码还比较简单,下面就是本篇核心(旋转)

四 旋转

旋转的目的

  • 保持搜索树规则
  • 当先树由不平衡转变为平衡
  • 降低树的高度

1.左单旋

在这里插入图片描述
根据上面的图我们写出下代码。

Node* subR=panret->_right;
Node* subRL=subR->_left;parent->_right=subRl;
subRL->_parent=parent;subR->_left=parent;
parent->_parent=subR;

这里还有几个细节问题需要注意
第一点:subRL可能为空,那subRL->_parent=parent;就会有问题。我们要加一个条件判断。
第二点:subR的父亲节点还没有被重新指向,这就会导致下图
在这里插入图片描述
我们就要先保存parent的父亲节点,这又有了一个新的问题,就是parent是不是根节点,所以左单旋的完整代码如下

1.1 左单旋完整代码

void RotateL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;if (subRL)subRL->_parent = parent;subR->_left = parent;Node* ppnode = parent->_parent;parent->_parent = subR;if (parent == _root){_root = subR;subR->_parent = nullptr;}else{if (ppnode->_left == parent){ppnode->_left = subR;}else{ppnode->_right = subR;}subR->_parent = ppnode;}parent->_bf = 0;subR->_bf = 0;}

2 右单旋

右单旋就是左旋的变形,理解好左旋,右旋就很好理解了。
在这里插入图片描述
看图写出代码,再根据左旋的注意事项,补全代码的逻辑。

2.2 右单旋完整代码

void RotateR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;parent->_left = subLR;if (subLR)subLR->_parent = parent;subL->_right = parent;Node* ppnode = parent->_parent;parent->_parent = subL;if (parent == _root){_root = subL;subL->_parent = nullptr;}else{if (ppnode->_left == parent){ppnode->_left = subL;}else{ppnode->_right = subL;}subL->_parent = ppnode;}subL->_bf = 0;parent->_bf = 0;}

3 双旋一(左+右)

在这里插入图片描述

注意看图,先对subL进行了左单旋,再对整棵树进行了右单旋。
注意:双旋对比单旋多了旋转结束之后平衡因子不是固定的,我们要风分情况把所有的可能性都写出来。
在这里插入图片描述
观察上图,我们发现subRL的平衡因子不同分别为:-1 1 0,这就是我们的解决方案。我们再画出旋转之后的图片。
在这里插入图片描述
分析完之后,就到了最简单的代码环节

3.2左右双旋完整代码

void RotateLR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;int bf = subLR->_bf;RotateL(parent->_left);RotateR(parent);if (bf == -1){subLR->_bf = 0;subL->_bf = 0;parent->_bf = 1;}else if (bf == 1){subLR->_bf = 0;subL->_bf = -1;parent->_bf = 0;}else if (bf == 0){subLR->_bf = 0;subL->_bf = 0;parent->_bf = 0;}else{assert(false);}}

4 双旋二(右+左)

在这里插入图片描述

还是先画出一般图观察,对subR进行右旋,再对整体左旋。
下面的分析与上面的分析类似,我就把图片放在下面,供大家参考。
在这里插入图片描述

4.2 右左双旋完整代码

void RotateRL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;int bf = subRL->_bf;RotateR(subR);RotateL(parent);subRL->_bf = 0;if (bf == 1){subR->_bf = 0;parent->_bf = -1;}else if (bf == -1){parent->_bf = 0;subR->_bf = 1;}else{parent->_bf = 0;subR->_bf = 0;}}

现在再去看上面的更新平衡因子的代码就比较清晰了。

旋转总结

左旋:新节点插入了较高子树的
右旋:新节点插入了较高子树的
双旋:
左+右:新节点插入了较高子树的
右+左:新节点插入了较高子树的

总之一句话:理解旋转我们一定要自己去画图,一定要自己动手,才会理解深刻。

五 验证AVL树的正确性

我们要写一个函数来判断这颗树符不符合AVL树。

	bool _IsBalance(Node* root, int& height){if (root == nullptr){height = 0;return true;}int leftHeight = 0, rightHeight = 0;if (!_IsBalance(root->_left, leftHeight)|| !_IsBalance(root->_right, rightHeight)){return false;}if (abs(rightHeight - leftHeight) >= 2){cout << root->_kv.first << "不平衡" << endl;return false;}if (rightHeight - leftHeight != root->_bf){cout << root->_kv.first << "平衡因子异常" << endl;return false;}height = leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;return true;}

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

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

相关文章

vim相关指令

vim的各种模式及其转换关系图 vim 默认处于命令模式&#xff01;&#xff01;&#xff01; 模式之间转换的指令 除【命令模式】之外&#xff0c;其它模式要切换到【命令模式】&#xff0c;只需要无脑 ESC 即可&#xff01;&#xff01;&#xff01; [ 命令模式 ] 切换至 [ 插…

联合体共用体--第二十三天

1.结构体元素有各自单独的空间 共用体元素共享空间&#xff0c;空间大小由最大类型确定 2.结构体元素互不影响&#xff0c;共用体赋值会导致覆盖

javaWeb智能医疗管理系统

简介 在当今快节奏的生活中&#xff0c;智能医疗系统的崛起为医疗行业带来了一场革命性的变革。基于JavaWeb技术开发的智能医疗管理系统&#xff0c;不仅为医疗机构提供了高效、精准的管理工具&#xff0c;也为患者提供了更便捷、更个性化的医疗服务。本文将介绍一个基于SSM&a…

一些重新开始面试之后的八股文汇总

一、内存中各项名词说明 1、机器内存概念说明 linux中的free命令可以查看机器的内存使用情况&#xff0c;vmstat命令也可以 其中不容易被理解的是&#xff1a; 内存缓冲/存数&#xff08;buffer/cached&#xff09; 1.buffers和cache也是RAM划分出来的一部分地址空间 2.buff…

SQL优化——统计信息

文章目录 1、统计信息1.1、表的统计信息1.2、列的统计信息1.3、索引的统计信息 2、统计信息重要参数设置3、检查统计信息是否过期4、扩展统计信息5、动态采样6、定制统计信息收集策略 只有大表才会产生性能问题&#xff0c;那么怎么才能让优化器知道某个表多大呢&#xff1f;这…

移动硬盘故障解析:NTFS属性0字节,双击无法访问的数据恢复之道

一、故障现象&#xff1a;移动硬盘NTFS属性0字节&#xff0c;双击无法访问 在日常使用移动硬盘的过程中&#xff0c;有时我们会遇到一个令人困惑的问题&#xff1a;移动硬盘在接入电脑后&#xff0c;显示其属性为NTFS格式&#xff0c;但容量却显示为0字节&#xff0c;且双击无…

【Delphi 爬虫库 1】GET和POST方法

文章目录 1.最简单的Get方法实现2.可自定义请求头、自定义Cookie的Get方法实现3.提取响应协议头4.Post方法实现单词翻译 爬虫的基本原理是根据需求获取信息并返回。就像当我们感到饥饿时&#xff0c;可以选择自己烹饪食物、外出就餐&#xff0c;或者订外卖一样。在编程中&#…

airtest-ios真机搭建实践

首先阅读4 ios connection - Airtest Project Docs 在Windows环境下搭建Airtest对iOS真机进行自动化测试的过程相对复杂&#xff0c;因为iOS的自动化测试通常需要依赖Mac OS系统&#xff0c;但理论上借助一些工具和服务&#xff0c;Windows用户也可以间接完成部分工作。下面是…

软考中级工程师网络技术第二节网络体系结构

OSPF将路由器连接的物理网络划分为以下4种类型&#xff0c;以太网属于&#xff08;25&#xff09;&#xff0c;X.25分组交换网属于&#xff08;非广播多址网络NBMA&#xff09;。 A 点对点网络 B 广播多址网络 C 点到多点网络 D 非广播多址网络 试题答案 正确答案&#xff1a; …

数据结构初阶:二叉树(二)

二叉树链式结构的实现 前置说明 在学习二叉树的基本操作前&#xff0c;需先要创建一棵二叉树&#xff0c;然后才能学习其相关的基本操作。由于现在对二叉树结构掌握还不够深入&#xff0c;为了降低学习成本&#xff0c;此处手动快速创建一棵简单的二叉树&#xff0c;快速进入二…

学习Rust的第5天:控制流

Control flow, as the name suggests controls the flow of the program, based on a condition. 控制流&#xff0c;顾名思义&#xff0c;根据条件控制程序的流。 If expression If表达式 An if expression is used when you want to execute a block of code if a condition …

list基础知识

list 1.list 的定义和结构 list 是双向链表&#xff0c;是C的容器模板&#xff0c;其接收两个参数&#xff0c;即 list(a,b) 其中 a 表示指定容器中存储的数据类型&#xff0c;b 表示用于分配器内存的分配器类型&#xff0c;默认为 list <int>; list 的特点&#xff1a;…

springboot中mongodb连接池配置-源码分析

yml下spring.data.mongodb 以前mysql等在spring.xxx下配置&#xff0c;现在springboot新版本&#xff08;小编3.2.3&#xff09;在spring.data.xxx下了&#xff0c;如下所示&#xff0c;mongodb的配置在spring.data.mongodb下&#xff1a; 连接池相关参数配置-源码分析 拼接在…

STM32应用开发——BH1750光照传感器详解

STM32应用开发——BH1750光照传感器详解 目录 STM32应用开发——BH1750光照传感器详解前言1 硬件介绍1.1 BH1750简介1.2 硬件接线 2 软件编程2.1 软件原理2.1.1 IIC设备地址2.1.2 IIC读写2.1.3 BH1750指令集2.1.4 BH1750工作流程2.1.5 BH1750测量模式 2.2 测试代码2.3 运行测试…

学习了解大模型的四大缺陷

由中国人工智能学会主办的第十三届吴文俊人工智能科学技术奖颁奖典礼暨2023中国人工智能产业年会于2024年4月14日闭幕。 会上&#xff0c;中国工程院院士、同济大学校长郑庆华认为&#xff0c;大模型已经成为当前人工智能的巅峰&#xff0c;大模型之所以强&#xff0c;是依托了…

Java 设计模式系列:模板方法模式

简介 模板方法模式是一种行为型设计模式&#xff0c;它定义一个操作中的算法骨架&#xff0c;将一些步骤推迟到子类中。模板方法模式使得子类可以不改变一个算法的结构&#xff0c;即可重定义该算法的某些特定步骤。 在模板方法模式中&#xff0c;抽象类中定义了一系列基本操…

48.基于SpringBoot + Vue实现的前后端分离-雪具销售系统(项目 + 论文PPT)

项目介绍 本站是一个B/S模式系统&#xff0c;采用SpringBoot Vue框架&#xff0c;MYSQL数据库设计开发&#xff0c;充分保证系统的稳定性。系统具有界面清晰、操作简单&#xff0c;功能齐全的特点&#xff0c;使得基于SpringBoot Vue技术的雪具销售系统设计与实现管理工作系统…

精通技术写作:如何写出高质量技术文章?

CSDN 的朋友你们好&#xff0c;我是未来&#xff0c;今天给大家带来专栏【程序员博主教程&#xff08;完全指南&#xff09;】的第 7 篇文章“如何撰写高质量技术文章”。本文深入探讨了如何写好一篇技术文章。文章给出了好的技术文章的定义和分析&#xff0c;并提供了从选题、…

MDK stm32怎么生成bin文件

第一种 D:\Keil_v5\ARM\ac5.6\bin\fromelf.exe --bin -o ../../Output/atk_f407.bin ../../Output/atk_f407.axf 空格解析 D:\Keil_v5\ARM\ac5.6\bin\fromelf.exe一个空格--bin一个空格-o两个空格../../Output/atk_f407.bin ../../Output/atk_f407.axf &#xff08;注意后…

回归预测 | Matlab实现GWO-GPR灰狼算法优化高斯过程回归多变量回归预测

回归预测 | Matlab实现GWO-GPR灰狼算法优化高斯过程回归多变量回归预测 目录 回归预测 | Matlab实现GWO-GPR灰狼算法优化高斯过程回归多变量回归预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 Matlab实现GWO-GPR灰狼算法优化高斯过程回归多变量回归预测 1.Matlab实现…