C++模拟实现AVL树

目录

1.文章概括

2.AVL树概念

3.AVL树的性质

4.AVL树的插入

5.旋转控制

1.左单旋

2. 右单旋

3.左右双旋

4.右左双旋

6.全部代码


1.文章概括

        本文适合理解平衡二叉树的读者阅读,因为AVL树是平衡二叉树的一种优化,其大部分实现逻辑与平衡二叉树是相同的,相同的部分不做过多阐述。

2.AVL树概念

        平衡二叉树主要用于查找数据,可提高查找效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下。在这样的缺点下,AVL树被发明了出来。AVL树的优化点在于:当向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之差的绝对值不超过1(需要对树中的结点进行调整),即可降低树的高度,从而减少平均搜索长度。

3.AVL树的性质

        一棵AVL树或者是空树,或者是具有以下性质的二叉搜索树:

1.它的左右子树都是AVL树;

2.左右子树高度之差(简称平衡因子)的绝对值不超过1(-1,0,1)。

AVL树 == 高度平衡二叉搜索树,说是平衡,为什么不是相等?

因为高度差不超过1,不是高度相等。

AVL树图片示例:

        如此控制后,增删改查的时间复杂度即为高度次 == O(logN)。

4.AVL树的插入

        对于一个结点的插入,分析如下:

1.新增在该结点的左,parent平衡因子减减;

2.新增在该结点的右,parent平衡因子加加;

3.更新后parent平衡因子 == 0,说明parent所在的子树的高度不变,不会再影响祖先,不用再沿着到root的路径往上更新;

4.更新后parent平衡因子 == 1 or -1,说明parent所在的子树的高度变化,会再影响祖先,需要沿着到root的路径往上更新;

5.更新后parent平衡因子 == 2 or -2,说明parent所在的子树的高度变化且不平衡,对parent所在子树进行旋转,让它平衡;

6.更到根结点。

补充说明:执行到4时会从1,2,开始继续循环,执行到3,5,6时插入结束。

旋转时需要注意的问题:

1.保持它是搜索树;

2.变成平衡树且降低这个子树的高度。

代码:

bool Insert(const T& data){Node* parent = nullptr;Node* cur = _pRoot;if (cur == nullptr){cur = new Node(data);_pRoot = cur;}while (cur){if (cur->_data > data){parent = cur;cur = cur->_pLeft;}else if (cur->_data < data){parent = cur;cur = cur->_pRight;}elsereturn false;}cur = new Node(data);if (parent->_data > data){parent->_pLeft = cur;cur->_pParent = parent;}else{parent->_pRight = cur;cur->_pParent = parent;}while (parent){if (parent->_pLeft == cur)parent->_bf--;elseparent->_bf++;if (parent->_bf == 0)return true;else if (parent->_bf == 1 || parent->_bf == -1){cur = parent;parent = parent->_pParent;}else{if (cur->_bf == 1 && parent->_bf == 2){RotateL(parent);}else if (cur->_bf == -1 && parent->_bf == -2){RotateR(parent);}else if (cur->_bf == -1 && parent->_bf == 2){RotateRL(parent);}else if (cur->_bf == 1 && parent->_bf == -2){RotateLR(parent);}break;}}}

        代码中,我使用了平衡因子来控制这棵树的高度。

5.旋转控制

1.左单旋

新节点插入较高右子树的右侧 --- 右右:

2. 右单旋

新节点插入较高左子树的左侧 --- 左左

3.左右双旋

 新节点插入较高左子树的右侧---左右:

4.右左双旋

新节点插入较高右子树的左侧 --- 右左:

6.全部代码

#pragma once
#include <iostream>
#include <assert.h>
#include <vector>
using namespace std;template<class T>
struct AVLTreeNode
{AVLTreeNode(const T& data = T()): _pLeft(nullptr), _pRight(nullptr), _pParent(nullptr), _data(data), _bf(0){}AVLTreeNode<T>* _pLeft;AVLTreeNode<T>* _pRight;AVLTreeNode<T>* _pParent;T _data;int _bf;   // 节点的平衡因子
};// AVL: 二叉搜索树 + 平衡因子的限制
template<class T>
class AVLTree
{typedef AVLTreeNode<T> Node;
public:AVLTree(): _pRoot(nullptr){}// 在AVL树中插入值为data的节点bool Insert(const T& data){Node* parent = nullptr;Node* cur = _pRoot;if (cur == nullptr){cur = new Node(data);_pRoot = cur;}while (cur){if (cur->_data > data){parent = cur;cur = cur->_pLeft;}else if (cur->_data < data){parent = cur;cur = cur->_pRight;}elsereturn false;}cur = new Node(data);if (parent->_data > data){parent->_pLeft = cur;cur->_pParent = parent;}else{parent->_pRight = cur;cur->_pParent = parent;}while (parent){if (parent->_pLeft == cur)parent->_bf--;elseparent->_bf++;if (parent->_bf == 0)return true;else if (parent->_bf == 1 || parent->_bf == -1){cur = parent;parent = parent->_pParent;}else{if (cur->_bf == 1 && parent->_bf == 2){RotateL(parent);}else if (cur->_bf == -1 && parent->_bf == -2){RotateR(parent);}else if (cur->_bf == -1 && parent->_bf == 2){RotateRL(parent);}else if (cur->_bf == 1 && parent->_bf == -2){RotateLR(parent);}break;}}}// AVL树的验证bool IsAVLTree(){return _IsAVLTree(_pRoot);}private:// 根据AVL树的概念验证pRoot是否为有效的AVL树bool _IsAVLTree(Node* root){if (root == nullptr)return true;int leftHight = Height(root->_pLeft);int rightHight = Height(root->_pRight);if (rightHight - leftHight != root->_bf){cout << "平衡因子异常:" << root->_data << "->" << root->_bf << endl;return false;}return abs(rightHight - leftHight) < 2&& _IsAVLTree(root->_pLeft)&& _IsAVLTree(root->_pRight);}size_t Height(Node* root){if (root == nullptr)return 0;int leftHeight = Height(root->_pLeft);int rightHeight = Height(root->_pRight);return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;}// 右单旋void RotateR(Node* pParent) {Node* cur = pParent->_pLeft;Node* curRight = cur->_pRight;Node* parentP = pParent->_pParent;pParent->_pLeft = curRight;if(curRight)curRight->_pParent = pParent;cur->_pRight = pParent;pParent->_pParent = cur;if (parentP == nullptr){_pRoot = cur;cur->_pParent = nullptr;}else{cur->_pParent = parentP;if (parentP->_pLeft == pParent)parentP->_pLeft = cur;elseparentP->_pRight = cur;}if(curRight)curRight->_bf = 0;pParent->_bf = cur->_bf = 0;}// 左单旋void RotateL(Node* pParent){Node* cur = pParent->_pRight;Node* curLeft = cur->_pLeft;Node* parentP = pParent->_pParent;pParent->_pRight = curLeft;if(curLeft)curLeft->_pParent = pParent;cur->_pLeft = pParent;pParent->_pParent = cur;if (parentP == nullptr){_pRoot = cur;cur->_pParent = nullptr;}else{cur->_pParent = parentP;if (parentP->_pLeft == pParent)parentP->_pLeft = cur;elseparentP->_pRight = cur;}if(curLeft)curLeft->_bf = 0;pParent->_bf = cur->_bf = 0;}// 右左双旋void RotateRL(Node* pParent){Node* cur = pParent->_pRight;Node* curLeft = cur->_pLeft;int temp = curLeft->_bf;RotateR(cur);RotateL(pParent);if (temp == 0)return;else if (temp == 1){pParent->_bf = -1;cur->_bf = 0;curLeft->_bf = 0;}else if (temp == -1){pParent->_bf = 0;cur->_bf = 1;curLeft->_bf = 0;}elseassert(false);}// 左右双旋void RotateLR(Node* pParent){Node* cur = pParent->_pLeft;Node* curRight = cur->_pRight;int temp = curRight->_bf;RotateL(cur);RotateR(pParent);if (temp == 0)return;else if (temp == 1){pParent->_bf = 0;cur->_bf = -1;curRight->_bf = 0;}else if (temp == -1){pParent->_bf = 1;cur->_bf = 0;curRight->_bf = 0;}elseassert(false);}private:Node* _pRoot;
};

  

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

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

相关文章

python-leetcode 25.环形链表

题目&#xff1a; 给定一个链表的头节点head,判断链表中是否有环。 如果链表中有某个节点&#xff0c;可以通过连续跟踪next指针再次到达&#xff0c;则链表中存在环。为了表示给定链表中的环&#xff0c;评测系统内部使用整数pos来表示链表尾连接到链表中的位置&#xff08;…

从云原生到 AI 原生,谈谈我经历的网关发展历程和趋势

作者&#xff1a;谢吉宝&#xff08;唐三&#xff09; 编者按&#xff1a; 云原生 API 网关系列教程即将推出&#xff0c;欢迎文末查看教程内容。本文整理自阿里云智能集团资深技术专家&#xff0c;云原生产品线中间件负责人谢吉宝&#xff08;唐三&#xff09; 在云栖大会的精…

SQL Server安装流程

SQL Server 2022在安全性、可用性和性能方面不断创新&#xff0c;是现在最支持Azure的SQL Server版本。 SQL Server发展史 SQL Server的历史始于1989年&#xff0c;当时是由微软与Sybase合作的产品&#xff0c;旨在为Windows NT操作系统提供一个高性能的数据库解决方案。随着…

C# 上位机--变量

C# 上位机--变量 在 C# 上位机开发领域&#xff0c;变量是构建程序逻辑的基础元素之一。它就像是一个容器&#xff0c;用于存储各种类型的数据&#xff0c;从简单的数值到复杂的对象。正确理解和使用变量&#xff0c;对于开发出高效、稳定且易于维护的上位机程序至关重要。本文…

Vue3(1)

一.create-vue // new Vue() 创建一个应用实例 > createApp() // createRouter() createStore() // 将创建实例进行了封装&#xff0c;保证每个实例的独立封闭性import { createApp } from vue import App from ./App.vue// mount 设置挂载点 #app (id为app的盒子) createA…

Redis 数据类型 List 列表

列表类型是⽤来存储多个有序的字符串&#xff0c;如下图所⽰&#xff0c;a、b、c、d、e 五个元素从左到右组成了⼀个有序的列表&#xff0c;列表中的每个字符串称为元素&#xff08;element&#xff09;&#xff0c;⼀个列表最多可以存储 2^32 - 1个元素。在 Redis 中&#xff…

yum报错 Could not resolve host: mirrorlist.centos.org

检查dns 使用ping www.baidu.com &#xff0c;如果ping不通&#xff0c;检查/etc/resolv.conf文件中是否有&#xff1a; nameserver 8.8.8.8 nameserver 8.8.4.4 替换yum源 1.备份原始的 YUM 源配置文件&#xff1a; sudo cp /etc/yum.repos.d/CentOS-Base.repo /etc/yum.r…

STM32F103C8----外部中断探秘:解锁嵌入式实时响应的关键

​​​ 一、引言 在嵌入式系统的广袤世界里&#xff0c;中断就如同一位高效的调度员&#xff0c;发挥着举足轻重的作用。想象一下&#xff0c;一个嵌入式系统就像一个繁忙的工厂&#xff0c;CPU 如同工厂里的核心工人&#xff0c;负责执行各种任务。如果没有中断机制&#x…

分层解耦-IOC DI 入门

步骤 ①.Service层及 Dao层的实现类&#xff0c;交给I0C容器管理。 ②.为Controller及Service注入运行时&#xff0c;依赖的对象。 ③.运行测试。 添加注解进行分层耦合 Component 会将当前类交给IOC容器管理,成为IOC容器中的bean - 控制反转 Autowired 运行时,IOC容器…

SQL Server 逻辑查询处理阶段及其处理顺序

在 SQL Server 中&#xff0c;查询的执行并不是按照我们编写的 SQL 语句的顺序进行的。相反&#xff0c;SQL Server 有自己的一套逻辑处理顺序&#xff0c;这个顺序决定了查询的执行方式和结果集的生成。了解这些处理阶段和顺序对于优化查询性能和调试复杂查询非常重要。 SQL …

问题:通过策略模式+工厂模式+模板方法模式实现ifelse优化

项目场景&#xff1a; 提示&#xff1a;这里简述项目相关背景&#xff1a; 示例&#xff1a;商城系统有会员系统&#xff0c;不同会员有不同优惠程度&#xff0c;普通会员不优惠&#xff1b;黄金会员打8折&#xff1b;白金会员优惠50元&#xff0c;再打7折&#xff1b; 问题描…

MYSQL利用PXC实现高可用

PXC常用端口 3306&#xff1a;数据库对外服务端口号 4444&#xff1a;请求SST的端口 4567&#xff1a;组成员之间进行沟通的端口号 4568&#xff1a;用于传输IST 搭建PXC集群 服务配置&#xff1a; 主机系统&#xff1a;rocky8.0 主机1&#xff1a;172.25.254.101 主机…

2.11寒假作业

web&#xff1a;[SWPUCTF 2022 新生赛]js_sign 打开环境是这样的&#xff0c;随便输入进行看看 提示错误&#xff0c;看源码其中的js代码 这个代码很容易理解&#xff0c;要让输入的内容等于对应的字符串&#xff0c;显然直接复制粘贴是错的 这串字符看起来像是base64加密&…

innovus如何分步长func和dft时钟

在Innovus工具中&#xff0c;分步处理功能时钟&#xff08;func clock&#xff09;和DFT时钟&#xff08;如扫描测试时钟&#xff09;需要结合设计模式&#xff08;Function Mode和DFT Mode&#xff09;进行约束定义、时钟树综合&#xff08;CTS&#xff09;和时序分析。跟随分…

《DeepSeek技术应用与赋能运营商办公提效案例实操落地课程》

大模型算法实战专家—周红伟老师 法国科学院数据算法博士/曾任阿里巴巴人工智能专家/曾任马上消费企业风控负责人 课程背景 随着大模型技术的迅猛发展&#xff0c;企业面临着提升工作效率、降低运营成本和优化资源配置的巨大压力。DeepSeek做出十三项革命性的大模型技术突破…

大模型基本原理(二)——ChatGPT的工作原理

如何得到一个ChatGPT&#xff1f; 1、无监督预训练&#xff1a;通过大量的文本数据集进行无监督训练&#xff0c;得到一个基座模型&#xff08;只会续写文本&#xff09; 2、监督微调&#xff1a;通过一些人类撰写的高质量对话数据对基座模型进行监督微调&#xff0c;得到一个…

示例代码:C# MQTTS双向认证(客户端)(服务器EMQX)

初级代码游戏的专栏介绍与文章目录-CSDN博客 我的github&#xff1a;codetoys&#xff0c;所有代码都将会位于ctfc库中。已经放入库中我会指出在库中的位置。 这些代码大部分以Linux为目标但部分代码是纯C的&#xff0c;可以在任何平台上使用。 源码指引&#xff1a;github源…

mosquitto配置桥接

同一终端中两个broker&#xff0c;其中一个做桥将1883端口的消息导出到1884&#xff1a; mosq.conf 多个服务器搭建mosquitto集群&#xff1a; mosquitto配置桥接_mosquitto 桥接-CSDN博客

通过Chatbox和API实现本地使用DeepSeek(R1满血版)

1、注册用户&#xff0c;申请API DeepSeek满血版api注册链接&#xff08;注册即送2000万Token&#xff09; 1.1 注册&#xff1a;https://cloud.siliconflow.cn/i/yl6uVodF 1.2 注册完成之后&#xff0c;申请API密钥 2、下载Chatbox 2.1 下载安装包&#xff1a;https://cha…

[学习笔记] Kotlin Compose-Multiplatform

Compose-Multiplatform 原文&#xff1a;https://github.com/zimoyin/StudyNotes-master/blob/master/compose-multiplatform/compose.md Compose Multiplatform 是 JetBrains 为桌面平台&#xff08;macOS&#xff0c;Linux&#xff0c;Windows&#xff09;和Web编写Kotlin UI…