深入理解AVL树及其旋转操作

AVL树的概念

二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单枝树,查找元素相当于在顺序表中搜索元素,效率低下。因此,两位俄罗斯的数学家G.M.Adelson-Velskii和E.M.Landis在1962年发明了一种方法:当二叉树搜索树中插入新节点后,如果能保证每个系欸但的左右子树的高度之差的绝对值不超过1,也就是说要降低树的高度,从而减少平均搜索长度。

一棵AVL树,它具有以下的性质:

  • 它的左右子树都是AVL树
  • 左右子树高度之差的绝对值不超过1

如果一棵搜索二叉树的高度是平衡的,它就是AVL树。如果它有n个结点,其高度可保持在log2(n),搜索时间复杂度log2(n)。

AVL树结点的定义

struct AVLTreeNode
{AVLTreeNode<K, V>* _left;//左孩子AVLTreeNode<K, V>* _right;//右孩子AVLTreeNode<K, V>* _parent;//父节点std::pair<K, V> _kv;//键值对int _bf;//平衡因子AVLTreeNode(const std::pair<K, V>& kv) : _left(nullptr),_right(nullptr),_parent(nullptr),_kv(kv),_bf(0){}
};

这里定义了对AVL树的结点的描述,一个结点应当有左右孩子结点,父节点,键值对,以及平衡因子
左右孩子结点和键值对是基于搜索二叉树(AVL树是在搜索二叉树的基础上建立的)。
平衡因子用来衡量这颗树是否是平衡的。
平衡因子=右子树高度-左子树高度
至于父节点,则是未来方便平衡因子的更新而添加进去的。

AVL树的插入

AVL树是在搜索二叉树的基础上引入了平衡因子,因此AVL树也可以看成是二叉搜索树。所以,插入的过程可以分为两步:

  1. 按照搜索二叉树的方式插入新节点
  2. 调整结点的平衡因子
bool Insert(const std::pair<K,V>&kv)
{Node* cur = _root;Node* parent = nullptr;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 == nullptr){_root = cur;return true;}if (parent->_kv.first < kv.first){parent->_right = cur;}elseparent->_left = cur;cur->_parent = parent;return true;
}

按照搜索二叉树的规则,查找到我们需要插入的地方。

  • 插入的键比当前结点的键小,递归左子树
  • 插入的键比当前节点的键大,递归右子树
  • 插入的键等于当前节点的键,说明已经插入过了,本次插入是失败的
    然后开始new一个新的结点,把结点之前的父子关系处理好

AVL树的调整(旋转)

如果在一棵原本是平衡的AVL树中插入一个新的结点,可能会造成不平衡的情况,此时就必须要调整A树的结构,使其平衡话。AVL树的旋转大致分为四种,这里采用画图的方式来理解这四种旋转的方式:

左左:右单旋

在这里插入图片描述

右右:左单旋

在这里插入图片描述

左右:先左旋,后右旋

在这里插入图片描述
这里的插入情况不唯一,但是值得一提的是
在调整之后,subLR的左子树一定是充当subL的右子树,subLR的右子树一定是充当了parent的左子树,可以根据这个来计算平衡因子

右左:先右旋,后左旋

在这里插入图片描述
同左右情况,这里也有相似的结论:
再调整之后,subRL的左子树充当为parent的右子树,subRL的右子树充当为subR的左子树

AVL树的完整实现代码

#include <iostream>
#include <cassert>template <class K, class V>
struct AVLTreeNode
{AVLTreeNode<K, V>* _left;AVLTreeNode<K, V>* _right;AVLTreeNode<K, V>* _parent;std::pair<K, V> _kv;int _bf;//平衡因子AVLTreeNode(const std::pair<K, V>& kv) : _left(nullptr),_right(nullptr),_parent(nullptr),_kv(kv),_bf(0){}
};template <class K, class V>
class AVLTree
{typedef AVLTreeNode<K, V> Node;public:bool Insert(const std::pair<K,V>&kv){Node* cur = _root;Node* parent = nullptr;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 == nullptr){_root = cur;return true;}if (parent->_kv.first < kv.first){parent->_right = cur;}elseparent->_left = cur;cur->_parent = parent;//更新平衡因子while (parent){if (cur == parent->_left)parent->_bf--;elseparent->_bf++;if (parent->_bf==0)break;else if (parent->_bf == 1 || parent->_bf == -1){//说明之前是0,需要继续向上更新cur = parent;parent = parent->_parent;}else if (parent->_bf == 2 || parent->_bf == -2){//这里需要进行旋转平衡if (parent->_bf == -2 && cur->_bf == -1){RotateR(parent);}else if (parent->_bf == 2 && cur->_bf == 1){RotateL(parent);}else if (parent->_bf == -2 && cur->_bf == 1){RotateLR(parent);}else if (parent->_bf == 2 && cur->_bf == -1){RotateRL(parent);}break;}else {//理论上不可能出现这种情况assert(false);}}return true;}Node* Find(const K& key){Node* cur = _root;while (cur){if (cur->_kv.first < key){cur = cur->_right;}else if (cur->_kv.first > key){cur = cur->_left;}else{return cur;}}return nullptr;}void InOrder(){_InOrder(_root);}//右旋转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;_root->_parent = nullptr;}//某个子树的根节点else{subL->_parent = ppNode;if (ppNode->_left == parent){ppNode->_left = subL;}else {ppNode->_right = subL;}}parent->_bf = subL->_bf = 0;}//左单旋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;_root->_parent = nullptr;}else {subR->_parent = ppNode;if (parent == ppNode->_left){ppNode->_left = subR;}else {ppNode->_right = subR;}}parent->_bf = subR->_bf = 0;}void RotateRL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;int bf = subRL->_bf;RotateR(parent->_right);RotateL(parent);if (bf == 1){subR->_bf = 0;subRL->_bf = 0;parent->_bf = -1;}else if (bf == -1){subR->_bf = 1;subRL->_bf = 0;parent->_bf = 0;}else if (bf == 0){subR->_bf = 0;subRL->_bf = 0;parent->_bf = 0;}else{assert(false);}}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的左边插入subL->_bf = 0;subLR->_bf = 0;parent->_bf = 1;}else if (bf == 1){//说明是在subLR的右边插入subL->_bf = -1;subLR->_bf = 0;parent->_bf = 0;}else if (bf == 0) {subL->_bf = 0;subLR->_bf = 0;parent->_bf = 0;}else{assert(false);}}bool IsBalance(){return _IsBalance(_root);}int Height(){return _Height(_root);}
private:bool _IsBalance(Node*node){if (node == nullptr)return true;int leftHeight = _Height(node->_left);int rightHeight = _Height(node->_right);if (abs(leftHeight - rightHeight) >= 2)return false;//检查一下平衡因子是否是正确的if (rightHeight - leftHeight != node->_bf){std::cout << node->_kv.first << std::endl;return false;}return _IsBalance(node->_left) &&_IsBalance(node->_right);}int _Height(Node* node){if (node == nullptr)return 0;return std::max(_Height(node->_left), _Height(node->_right)) + 1;}void _InOrder(Node* node){//中序遍历if (node == nullptr)return;_InOrder(node->_left);std::cout << node->_kv.first << ": " << node->_kv.second << std::endl;_InOrder(node->_right);}
private:Node* _root = nullptr; // 根节点
};

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

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

相关文章

URL带有中文会引入哪些问题

处理含中文字符的 URL 1 为什么会出现“乱码”或崩溃&#xff1f; URL 标准&#xff08;RFC 3986&#xff09;规定&#xff1a;除少数保留字符外&#xff0c;URL 只能包含 ASCII。中文属于 Unicode&#xff0c;因此必须先转换。如果直接把 https://example.com/路径/ 这样的字…

结构体字段能否单独加 mut

你问的这个问题在 Rust 里很常见&#xff1a; 一、结构体字段能否单独加 mut 1. 结构体字段能否单独加 mut&#xff1f; 不能。Rust 中&#xff0c;mut 是用来修饰变量绑定的&#xff0c;可变性是绑定的属性&#xff0c;而不是结构体字段本身的属性。 你不能写&#xff1a; …

scGPT-spatial 复现

文章目录 ✅ 总体流程总览&#xff08;从 H5AD 到模型训练&#xff09;&#x1f527; 步骤 1&#xff1a;读取 H5AD 文件并做基础预处理&#x1f9f1; 步骤 2&#xff1a;构造训练样本输入&#xff08;token、value&#xff09;&#x1f4e6; 步骤 3&#xff1a;使用 DataColla…

运放电压跟随器为什么要加电阻

运放电压跟随器为什么要加电阻 我们常见运放的电压跟随器如下&#xff1a; 有时候会看见电路中加两个电阻&#xff1a; 作用就是保护运放&#xff0c;起限流电阻的作用。 当输入电压高的时候&#xff0c;运放内部存在钳位二极管&#xff0c;此电阻就能限流。 并不是所有运放…

MinerU 2.0部署

简介 MinerU 2.0使用sglang加速&#xff0c;与之前差别较大&#xff0c;建议按照官方的Docker镜像的方式启动。 Docker镜像 Dockerfile 这是官方的Dockerfile # Use the official sglang image FROM lmsysorg/sglang:v0.4.7-cu124# install mineru latest RUN python3 -m …

黑马python(十七)

目录&#xff1a; 1.数据可视化-地图-基础案例 2.全国疫情地图 3.河南省疫情地图绘制 4.基础柱状图构建 5.基础时间线柱状图绘制 6.动态GDP柱状图绘制 1.数据可视化-地图-基础案例 图示有点对的不准&#xff0c;可以通过后面的参数 2.全国疫情地图 3.河南省疫情地图绘制…

Segment Anything in High Quality之SAM-HQ论文阅读

摘要 最近的 Segment Anything Model(SAM)在扩展分割模型规模方面取得了重大突破,具备强大的零样本能力和灵活的提示机制。尽管 SAM 在训练时使用了 11 亿个掩码,其掩码预测质量在许多情况下仍不理想,尤其是对于结构复杂的目标。我们提出了 HQ-SAM,使 SAM 能够精确地分割…

深入理解_FreeRTOS的内部实现(2)

1.事件组 事件组结构体&#xff1a; 事件组 “不关中断” 的核心逻辑 事件组操作时&#xff0c;优先选择 “关调度器” 而非 “关中断” &#xff0c;原因和实现如下&#xff1a; 关调度器&#xff08;而非关中断&#xff09; FreeRTOS 提供 taskENTER_CRITICAL()&#xff08;…

【图论题典】Swift 解 LeetCode 最小高度树:中心剥离法详解

文章目录 摘要描述题解答案题解代码分析思路来源&#xff1a;树的“中心剥离法”构造邻接表和度数组循环剥叶子终止条件 示例测试及结果时间复杂度空间复杂度总结 摘要 树是一种重要的数据结构&#xff0c;在许多应用里&#xff0c;我们希望选一个根&#xff0c;让这棵树的高度…

Docker的介绍与安装

​ Docker 对初学者的简单解释和应用场景 1.什么是 Docker&#xff1f; 简单来说&#xff0c;Docker 就像一个“装箱子”的工具&#xff0c;这个箱子叫做“容器”。 你写的程序和它运行需要的环境&#xff08;比如操作系统、软件、工具&#xff09;都装进一个箱子里。这个箱…

引导相机:工业自动化的智能之眼,赋能制造业高效升级

在工业自动化浪潮中&#xff0c;精准的视觉引导技术正成为生产效率跃升的关键。作为迁移科技——一家成立于2017年、专注于3D工业相机和3D视觉系统的领先供应商&#xff0c;我们深知"引导相机"的核心价值&#xff1a;它不仅是一个硬件设备&#xff0c;更是连接物理世…

智能相机如何重塑工业自动化?迁移科技3D视觉系统的场景革命

从硬件参数到产业价值&#xff0c;解码高精度视觉系统的落地逻辑 一、工业视觉的“智慧之眼” 迁移科技深耕3D工业相机领域&#xff0c;以“稳定、易用、高回报”为核心理念&#xff0c;打造覆盖硬件、算法、软件的全栈式视觉系统。成立6年累计融资数亿元的背后&#xff0c;是…

【数据挖掘】聚类算法学习—K-Means

K-Means K-Means是一种经典的无监督学习算法&#xff0c;用于将数据集划分为K个簇&#xff08;clusters&#xff09;&#xff0c;使得同一簇内的数据点相似度高&#xff0c;不同簇间的相似度低。它在数据挖掘、模式识别和机器学习中广泛应用&#xff0c;如客户细分、图像压缩和…

linux环境内存满php-fpm

检查 PHP-FPM 配置 pm.max_children&#xff1a;该参数控制 PHP-FPM 进程池中最大允许的子进程数。过高的子进程数会导致内存占用过大。你可以根据服务器的内存大小来调整 pm.start_servers&#xff1a;控制 PHP-FPM 启动时创建的进程数。根据实际情况调整此值。 pm.min_spare_…

基于CNN卷积神经网络图像识别小程序9部合集

基于CNN卷积神经网络图像识别小程序合集-视频介绍下自取 ​ 内容包括&#xff1a; 基于python深度学习的水果或其他物体识别小程序 003基于python深度学习的水果或其他物体识别小程序_哔哩哔哩_bilibili 代码使用的是python环境pytorch深度学习框架&#xff0c;代码的环境安…

WebRTC(九):JitterBuffer

JitterBuffer Jitter “Jitter”指的是连续到达的媒体包之间时间间隔的变化。在网络传输中&#xff0c;由于&#xff1a; 网络拥塞路由路径变化队列排队不同链路带宽差异 导致包之间的接收时间不一致&#xff0c;这就是网络“抖动”。 作用 **JitterBuffer&#xff08;抖…

【推荐100个unity插件】在 Unity 中绘制 3D 常春藤,模拟生长——hedera插件的使用

注意&#xff1a;考虑到后续接触的插件会越来越多&#xff0c;我将插件相关的内容单独分开&#xff0c;并全部整合放在【推荐100个unity插件】专栏里&#xff0c;感兴趣的小伙伴可以前往逐一查看学习。 效果演示 文章目录 效果演示前言一、常春藤生成器工具下载二、工具使用1、…

【三维重建】【3DGS系列】【深度学习】3DGS的理论基础知识之高斯椭球的几何变换

【三维重建】【3DGS系列】【深度学习】3DGS的理论基础知识之高斯椭球的几何变换 文章目录 【三维重建】【3DGS系列】【深度学习】3DGS的理论基础知识之高斯椭球的几何变换前言模型变换(Model Transformation)观测变换(Viewing Transformation)视图变换(View Transformation)投影…

EXISTS 和 NOT EXISTS 、IN (和 NOT IN)

在 SQL 中&#xff0c;EXISTS、NOT EXISTS 和 IN 都是用于子查询的条件运算符&#xff0c;用于根据子查询的结果过滤主查询的行。它们之间的区别主要体现在工作方式、效率、对 NULL 值的处理以及适用场景上。 1. EXISTS 和 NOT EXISTS 作用&#xff1a; EXISTS: 检查子查询是…

GitHub 趋势日报 (2025年06月25日)

&#x1f4ca; 由 TrendForge 系统生成 | &#x1f310; https://trendforge.devlive.org/ &#x1f310; 本日报中的项目描述已自动翻译为中文 &#x1f4c8; 今日获星趋势图 今日获星趋势图 880 awesome 788 build-your-own-x 691 free-for-dev 427 best-of-ml-python 404 …