【数据结构】深入理解AVL树:实现和应用

AVL树是一种自平衡的二叉搜索树,它能够保持良好的平衡性质,使得在最坏情况下的时间复杂度也能保持在对数级别。本文将深入介绍AVL树的原理、实现和应用,并通过示例代码演示其基本操作。

文章目录

  • 什么是AVL树?
  • AVL树的实现
    • 在AVL树中插入值为data的节点实现:
    • AVL树的旋转
      • 右单旋
      • 左单旋
      • 左右双旋
      • 右左双旋
  • AVL树的应用
  • 完整代码
  • 总结

什么是AVL树?

AVL树是由两位苏联数学家Adelson-Velsky和Landis于1962年发明的,它的特点是能够在插入和删除节点时自动调整树的结构,以保持树的平衡性。在AVL树中,任意节点的左右子树高度差不超过1,这就保证了整棵树的高度始终保持在对数级别,从而保证了插入、删除和查找等操作的高效性。

AVL树的实现

以下是一个C++实现的AVL树的基本结构和操作示例:

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);// AVL树的验证bool IsAVLTree(){return _IsAVLTree(_pRoot);}private:// 根据AVL树的概念验证pRoot是否为有效的AVL树bool _IsAVLTree(Node* pRoot);size_t _Height(Node* pRoot);// 右单旋void RotateR(Node* pParent);// 左单旋void RotateL(Node* pParent);// 右左双旋void RotateRL(Node* pParent);// 左右双旋void RotateLR(Node* pParent);private:Node* _pRoot;
};

在AVL树中插入值为data的节点实现:

// 在AVL树中插入值为data的节点
bool Insert(const T& data) {// 插入节点if (_pRoot == nullptr) {_pRoot = new Node(data);return true;}Node* pParent = nullptr;Node* pCur = _pRoot;while (pCur) {pParent = pCur;if (data < pCur->_data)pCur = pCur->_pLeft; // 往左子树查找else if (data > pCur->_data) pCur = pCur->_pRight; // 往右子树查找elsereturn false; // 重复值不插入}// 创建新节点pCur = new Node(data);if (data < pParent->_data)pParent->_pLeft = pCur;elsepParent->_pRight = pCur;pCur->_pParent = pParent;// 插入节点后,更新平衡因子并进行平衡处理while (pParent) {if (pCur == pParent->_pLeft) // 更新节点在左子树--pParent->_bf; // 更新父节点的平衡因子else++pParent->_bf; // 更新父节点的平衡因子if (0 == pParent->_bf) // 如果平衡旋转结束break;// 如果父节点的bf==1或-1,则不需要调整,直接向上更新即可if (1 == pParent->_bf || -1 == pParent->_bf) {pCur = pParent;pParent = pParent->_pParent;} else { // 父节点不平衡,需要旋转调整if (pParent->_bf == 2) {if (pCur->_bf == 1) // LL型// 左单旋RotateL(pParent);else // LR型// 先左旋后右旋RotateRL(pParent);}else {if (pCur->_bf == -1) // RR型// 右单旋RotateR(pParent);else // RL型// 先左旋后右旋RotateLR(pParent);}break;}}return true;
}

AVL树的旋转

AVL树的平衡是通过维护每个节点的平衡因子来实现的。平衡因子指的是节点的左子树高度减去右子树高度的差值,其取值范围为-1、0和1。当平衡因子的绝对值超过1时,AVL树就需要进行旋转操作来重新平衡。

右单旋

在这里插入图片描述

// 右单旋void RotateR(Node* pParent) {Node* pSubL = pParent->_pLeft;Node* pSubLR = pSubL->_pRight;// 右旋pParent->_pLeft = pSubLR;if (pSubLR)pSubLR->_pParent = pParent;pSubL->_pRight = pParent;pSubL->_pParent = pParent->_pParent;pParent->_pParent = pSubL;if (pParent == _pRoot)_pRoot = pSubL;else {if (pSubL->_pParent->_pLeft == pParent)pSubL->_pParent->_pLeft = pSubL;elsepSubL->_pParent->_pRight = pSubL;}pParent->_bf = pSubL->_bf = 0;}

左单旋

在这里插入图片描述

// 左单旋
void RotateL(Node* pParent) {Node* pSubR = pParent->_pRight;Node* pSubRL = pSubR->_pLeft;// 左旋pParent->_pRight = pSubRL;if (pSubRL)pSubRL->_pParent = pParent;pSubR->_pLeft = pParent;pSubR->_pParent = pParent->_pParent;pParent->_pParent = pSubR;if (pParent == _pRoot)_pRoot = pSubR;else{if (pSubR->_pParent->_pLeft == pParent)pSubR->_pParent->_pLeft = pSubR;elsepSubR->_pParent->_pRight = pSubR;}pParent->_bf = pSubR->_bf = 0;
}

左右双旋

在这里插入图片描述

// 左右双旋
void RotateLR(Node* pParent) {// 先左旋后右旋Node* pSubL = pParent->_pLeft;Node* pSubLR = pSubL->_pRight;// 旋转之前,保存pSubLR的平衡因子,旋转完成之后,需要根据该平衡因子来调整其他节点的平衡因子int bf = pSubLR->_bf;// 进行左单旋RotateL(pParent->_pLeft);// 进行右单旋RotateR(pParent);if (1 == bf)pSubL->_bf = -1;else if (-1 == bf)pParent->_bf = 1;
}

右左双旋

在这里插入图片描述

// 右左双旋
void RotateRL(Node* pParent) {// 先右旋后左旋Node* pSubR = pParent->_pRight;Node* pSubRL = pSubR->_pLeft;// 旋转之前,保存pSubRL的平衡因子,旋转完成之后,需要根据该平衡因子来调整其他节点的平衡因子int bf = pSubRL->_bf;// 进行右单旋RotateR(pParent->_pRight);//进行左单旋RotateL(pParent);if (1 == bf)pParent->_bf = -1;else if (-1 == bf)pSubR->_bf = 1;
}

AVL树的应用

AVL树由于其高效的插入、删除和查找操作,在计算机科学领域有着广泛的应用。例如,在数据库系统中,AVL树常被用作索引结构,用于加速数据的检索操作;在编译器的符号表实现中,也可以使用AVL树来存储和查找变量信息。

完整代码

// AVLTree.h
#include <iostream>
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) {// 插入节点if (_pRoot == nullptr) {_pRoot = new Node(data);return true;}Node* pParent = nullptr;Node* pCur = _pRoot;while (pCur) {pParent = pCur;if (data < pCur->_data)pCur = pCur->_pLeft; // 往左子树查找else if (data > pCur->_data) pCur = pCur->_pRight; // 往右子树查找elsereturn false; // 重复值不插入}// 创建新节点pCur = new Node(data);if (data < pParent->_data)pParent->_pLeft = pCur;elsepParent->_pRight = pCur;pCur->_pParent = pParent;// 插入节点后,更新平衡因子并进行平衡处理while (pParent) {if (pCur == pParent->_pLeft) // 更新节点在左子树--pParent->_bf; // 更新父节点的平衡因子else++pParent->_bf; // 更新父节点的平衡因子if (0 == pParent->_bf) // 如果平衡旋转结束break;// 如果父节点的bf==1或-1,则不需要调整,直接向上更新即可if (1 == pParent->_bf || -1 == pParent->_bf) {pCur = pParent;pParent = pParent->_pParent;} else { // 父节点不平衡,需要旋转调整if (pParent->_bf == 2) {if (pCur->_bf == 1) // LL型// 左单旋RotateL(pParent);else // LR型// 先左旋后右旋RotateRL(pParent);}else {if (pCur->_bf == -1) // RR型// 右单旋RotateR(pParent);else // RL型// 先左旋后右旋RotateLR(pParent);}break;}}return true;}// AVL树的验证bool IsAVLTree(){return _IsAVLTree(_pRoot);}private:// 根据AVL树的概念验证pRoot是否为有效的AVL树bool _IsAVLTree(Node* pRoot) {if (pRoot == nullptr)return true;int leftHeight = _Height(pRoot->_pLeft);int rightHeight = _Height(pRoot->_pRight);if (abs(leftHeight - rightHeight) > 1)return false;return _IsAVLTree(pRoot->_pLeft) && _IsAVLTree(pRoot->_pRight);}size_t _Height(Node* pRoot) {if (pRoot == nullptr)return 0;int leftHeight = _Height(pRoot->_pLeft);int rightHeight = _Height(pRoot->_pRight);return 1 + max(leftHeight, rightHeight);}// 右单旋void RotateR(Node* pParent) {Node* pSubL = pParent->_pLeft;Node* pSubLR = pSubL->_pRight;// 右旋pParent->_pLeft = pSubLR;if (pSubLR)pSubLR->_pParent = pParent;pSubL->_pRight = pParent;pSubL->_pParent = pParent->_pParent;pParent->_pParent = pSubL;if (pParent == _pRoot)_pRoot = pSubL;else {if (pSubL->_pParent->_pLeft == pParent)pSubL->_pParent->_pLeft = pSubL;elsepSubL->_pParent->_pRight = pSubL;}pParent->_bf = pSubL->_bf = 0;}// 左单旋void RotateL(Node* pParent) {Node* pSubR = pParent->_pRight;Node* pSubRL = pSubR->_pLeft;// 左旋pParent->_pRight = pSubRL;if (pSubRL)pSubRL->_pParent = pParent;pSubR->_pLeft = pParent;pSubR->_pParent = pParent->_pParent;pParent->_pParent = pSubR;if (pParent == _pRoot)_pRoot = pSubR;else{if (pSubR->_pParent->_pLeft == pParent)pSubR->_pParent->_pLeft = pSubR;elsepSubR->_pParent->_pRight = pSubR;}pParent->_bf = pSubR->_bf = 0;}// 右左双旋void RotateRL(Node* pParent) {// 先右旋后左旋Node* pSubR = pParent->_pRight;Node* pSubRL = pSubR->_pLeft;// 旋转之前,保存pSubRL的平衡因子,旋转完成之后,需要根据该平衡因子来调整其他节点的平衡因子int bf = pSubRL->_bf;// 进行右单旋RotateR(pParent->_pRight);//进行左单旋RotateL(pParent);if (1 == bf)pParent->_bf = -1;else if (-1 == bf)pSubR->_bf = 1;}// 左右双旋void RotateLR(Node* pParent) {// 先左旋后右旋Node* pSubL = pParent->_pLeft;Node* pSubLR = pSubL->_pRight;// 旋转之前,保存pSubLR的平衡因子,旋转完成之后,需要根据该平衡因子来调整其他节点的平衡因子int bf = pSubLR->_bf;// 进行左单旋RotateL(pParent->_pLeft);// 进行右单旋RotateR(pParent);if (1 == bf)pSubL->_bf = -1;else if (-1 == bf)pParent->_bf = 1;}private:Node* _pRoot;
};
// test.cpp
#include <iostream>
#include <ctime>
#include <cstdlib>
#include "AVLTree.h"int main() {AVLTree<int> avlTree;const int NUM_VALUES = 1000000;// 生成并插入 10 万个随机整数值srand(static_cast<unsigned int>(time(nullptr)));for (int i = 0; i < NUM_VALUES; ++i) {int randomValue = rand() % 1000000; // 生成 0 到 999999 之间的随机整数avlTree.Insert(randomValue);}// 验证是否为 AVL 树if (avlTree.IsAVLTree()) {std::cout << "AVL Tree validation: This is an AVL tree." << std::endl;}else {std::cout << "AVL Tree validation: This is not an AVL tree." << std::endl;}return 0;
}

总结

本篇博客深入介绍了AVL树,包括其原理、实现和应用。通过C++代码示例展示了AVL树的基本结构和操作,以及探讨了在计算机科学领域的广泛应用。整体内容帮助读者更好地理解和应用AVL树这一自平衡的二叉搜索树。

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

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

相关文章

Vue工程化基础

一Ajax 1.1Ajax概述&#xff1a; 异步与同步 繁琐被淘汰了。 二Axios2 前后端混合开发&#xff1a; 前后端分离开发&#xff1a; YAPI 三前端开发工程化 四Vue脚手架 项目的认识 改变端口号 五Vue开发流程&#xff1a; 六Element组件 6.1快速入门 下载> npm install e…

阿里云数据库优惠价格99元1年起MySQL/SQL Server/PostgreSQL/Redis/MongoDB/MairaDB/ClickHouse

2024年阿里云数据库价格查询&#xff0c;云数据库优惠活动MySQL版2核2GB 50GB配置99元一年&#xff0c;续费不涨价&#xff0c;续费也是99元1年&#xff0c;云数据库MySQL基础系列经济版 2核4GB 100GB配置227元1年&#xff0c;RDS SQL Server云数据库2核4G配置299元1年&#xf…

自学rabbitmq入门到精通

交换机的fault &#xff08;发布与订阅模式&#xff09; 因为消息是由生产者发送给excahnge&#xff0c;exchange发送给队列&#xff0c; 然后由队列发送给消费者的。 展示使用图形化界面使用fanout模式。 创建交换机 然后创建三个队列&#xff0c;绑定对应的交换机&#xff…

深度学习pytorch——Broadcast自动扩展

介绍 在 PyTorch 中&#xff0c;Broadcast 是指自动扩展&#xff08;broadcasting&#xff09;运算的功能。它允许用户在不同形状的张量之间执行运算&#xff0c;而无需手动将它们的形状改变为相同的大小。当进行运算时&#xff0c;PyTorch 会自动调整张量的形状&#xff0c;使…

C# danbooru Stable Diffusion 提示词反推 Onnx Demo

目录 说明 效果 模型信息 项目 代码 下载 C# danbooru Stable Diffusion 提示词反推 Onnx Demo 说明 模型下载地址&#xff1a;https://huggingface.co/deepghs/ml-danbooru-onnx 效果 模型信息 Model Properties ------------------------- ----------------------…

深度学习 精选笔记(12)卷积神经网络-理论基础2

学习参考&#xff1a; 动手学深度学习2.0Deep-Learning-with-TensorFlow-bookpytorchlightning ①如有冒犯、请联系侵删。 ②已写完的笔记文章会不定时一直修订修改(删、改、增)&#xff0c;以达到集多方教程的精华于一文的目的。 ③非常推荐上面&#xff08;学习参考&#x…

鸿蒙Harmony应用开发—ArkTS声明式开发(容器组件:Counter)

计数器组件&#xff0c;提供相应的增加或者减少的计数操作。 说明&#xff1a; 该组件从API Version 7开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本。 子组件 可以包含子组件。 接口 Counter() 从API version 9开始&#xff0c;该接口…

SQL的数据定义语言(DDL)语句

文章目录 数据库操作创建新的数据库修改数据库删除数据库 表操作创建数据库表修改数据表删除数据表 索引操作创建索引修改索引 视图操作修改视图 序列操作创建序列修改序列删除序列 分区操作&#xff08;在支持分区的数据库中&#xff09;同义词操作&#xff08;在Oracle等数据…

使用map和set实现简单的词频统计

一、运行效果图 二、代码示例 #include <iostream> #include <fstream> #include <sstream> #include <string> #include <map> #include <set> #include <vector> #include <algorithm> using namespace std;class TextQuer…

LarkXR上新了 | Apollo多终端与XR体验的优化创新

作为领先的数字平行世界产品技术提供方&#xff0c;「Paraverse平行云」一直致力于为企业和开发者提供企业级实时云渲染解决方案。其多终端接入产品LarkXR Apollo&#xff0c;基于底层Runtime技术&#xff0c;实现了在Windows、Linux、MacOS、Android、iOS等多种操作系统下&…

游戏服务端配置“热更”及“秒启动”终极方案(golang/ygluu/卢益贵)

游戏服务端配置“热更”及“秒启动”终极方案 ygluu 卢益贵 关键词&#xff1a;游戏微服务架构、游戏服务端热更、模块化解耦、golang 目录 一、前言 二、异步线程加载/重载方案 三、配置表碎片化方案 四、指针间接引用 五、重载通知 六、示例代码 七、相关连接 一、…

Samtec科普 | 一文了解患者护理应用连接器

【摘要/前言】 通过医疗专业人士为患者提供护理的种种需求&#xff0c;已经不限于手术室与医院的各种安全状况。当今许多患者的护理都是在其他环境进行&#xff0c;例如医生办公室、健康中心&#xff0c;还有越来越普遍的住家。尤其是需要长期看护的患者&#xff0c;所需的科技…

202006A卷青少年软件编程(Scratch)等级考试试卷(三级)

第1题:【 单选题】 执行以下脚本后舞台上的角色将 ?( ) A:先克隆自身,克隆体出现后被删除 B:先克隆自身,克隆体出现后删除本体 C:克隆出自身后本体与克隆体同时被删除 D:克隆出自身后本体与克隆体被不会被删除 【正确答案】: A 【试题解析】 : 第2题:【 单选题】…

MATLAB环境下基于决策树和随机森林的心力衰竭患者生存情况预测

近年来&#xff0c;随着医学数据的不断积累和计算机技术的快速发展&#xff0c;许多机器学习技术已经被用在医学领域&#xff0c;并取得了不错的效果。与传统的基于医学知识经验的心衰预后评估模型相比&#xff0c;机器学习方法可以快速、高效地从繁杂的、海量的心衰病人数据中…

SSH 批量免密登录服务器

思路&#xff1a;创建密钥&#xff0c;并通过分发公钥文件到其他服务器&#xff0c;从而实现批量免密无交互登录服务器 拓扑结构&#xff1a;主服务器 |----》从服务器1 |----》从服务器2 |----》..... 参考链接&#xff1a;SSH命令批量操作服务器_ssh批量登录远程执行脚本…

【Liunx-后端开发软件安装】Liunx安装nginx

【Liunx-后端开发软件安装】Liunx安装nginx 使用安装包安装 一、简介 nginx&#xff0c;这个家伙可不是你厨房里的那位大厨&#xff0c;它可是互联网世界的“煎饼果子摊主”。想象一下&#xff0c;在熙熙攘攘的网络大街上&#xff0c;nginx挥舞着它的锅铲——哦不&#xff0c;是…

消息队列面试题

目录 1. 为什么使用消息队列 2. 消息队列的缺点 3. 消息队列如何选型&#xff1f; 4. 如何保证消息队列是高可用的 5. 如何保证消息不被重复消费&#xff08;见第二条&#xff09; 6. 如何保证消息的可靠性传输&#xff1f; 7. 如何保证消息的顺序性&#xff08;即消息幂…

总说上下文切换耗性能,那他到底耗了多少性能?

大家好&#xff0c;我是「云舒编程」&#xff0c;今天我们来聊聊上下文切换性能消耗。 文章首发于微信公众号&#xff1a;云舒编程 关注公众号获取&#xff1a; 1、大厂项目分享 2、各种技术原理分享 3、部门内推 一、前言 众所周知&#xff0c;操作系统是一个分时复用系统&…

.NET高级面试指南专题十九【 数据库设计-4范式】

数据库范式设计是关系数据库设计中的重要概念&#xff0c;旨在减少数据冗余和提高数据的一致性。 范式设计的目的是提高数据库的数据质量、一致性和可维护性。通过将数据结构化为不同的范式&#xff0c;可以降低数据冗余&#xff0c;减少数据更新异常&#xff0c;提高数据的可靠…

Java八股文(MyBatis Plus)

Java八股文のMyBatis Plus MyBatis Plus MyBatis Plus MyBatis Plus 是什么&#xff1f;它与 MyBatis 有什么区别&#xff1f; MyBatis Plus 是基于 MyBatis 进行扩展的一款持久层框架&#xff0c;它提供了一系列增强功能&#xff0c;简化了 MyBatis 的使用。 与 MyBatis 相比…