当前位置: 首页 > news >正文

AVL 树

文章目录

  • 一、AVL 树的概念
  • 二、AVL 树的实现
    • 1. AVL 树的存储结构
    • 2. AVL 树的插入

一、AVL 树的概念

在 二叉搜索树 中,当我们连续插入有序的数据时,二叉搜索树可能会呈现单枝树的情况,此时二叉搜索树的查找效率为 O(N)

俄罗斯的两位数学家 G. M. Adelson-Velsky 和 E. M. Landis 发明了 AVL 树可以解决上述问题,AVL 树保证树中的每个结点的左右子树高度差不会超过 1,从而保证 AVL 树是一颗高度平衡的二叉搜索树,从而保证 AVL 树的搜索效率为 O(log N),AVL 树的名字就是取自于这两位科学家

一颗 AVL 树是 空树 或者满足如下条件:

  • 左右子树的高度差小于等于 1 的二叉搜索树
  • 左右子树均为 AVL 树

AVL 树是一颗在二叉搜索树并且满足所有结点的左右子树高度差不超过 1
在这里插入图片描述

二、AVL 树的实现

AVL 树有很多实现方式,这里采用三叉链和平衡因子,结点的平衡因子的值为右子树的高度减去左子树的高度,通过控制所有结点的平衡因子的绝对值小于等于 1,并且保证该树为二叉搜索树,即可实现 AVL 树

1. AVL 树的存储结构

// AVL 树的结点
template<class K, class V>
struct AVLTreeNode
{std::pair<K, V> _kv;AVLTreeNode<K, V>* _parent;AVLTreeNode<K, V>* _left;AVLTreeNode<K, V>* _right;int _bf;	// 平衡因子: 右子树的高度前去左子树的高度AVLTreeNode<K, V>(const std::pair<K, V>& kv = std::pair<K, V>(K(), V())): _kv(kv), _parent(nullptr), _left(nullptr), _right(nullptr), _bf(0){}
};// AVL 树
template<class K, class V>
class AVLTree
{typedef AVLTreeNode<K, V> Node;
public:AVLTree<K, V>(): _root(nullptr){}private:Node* _root;
};

2. AVL 树的插入

首先按照二叉搜索树的方式插入结点,保证插入结点之后还是二叉搜索树,当插入结点完成之后,该结点的祖先结点的平衡因子可能会受到影响,如果插入结点在祖先结点的左子树中,则祖先结点的 _bf --,否则该结点的 _bf ++(平衡因子的值为右子树的高度减去左子树的高度)

祖先结点的 _bf 更新后,有三种情况 _bf == 0 和 _bf == -1 || _bf == 1 以及 _bf == -2 || _bf == 2

  • 当 _bf == 0 时:当前更新 _bf 的结点所在的子树高度没有变化,此时不用继续更新祖先结点的 _bf

如果插入结点在祖先结点的右子树,祖先结点的平衡因子从 -1 -> 0
如果插入结点在祖先节点的左子树,祖先结点的平衡因子从 1 -> 0

无论是这两种的那种情况,对于更新后 _bf == 0 的结点的祖先结点而言,子树的高度是没有变化的
在这里插入图片描述

  • 当 _bf == -1 || _bf == 1 时,当前更新 _bf 的结点所在的子树高度增加了,此时需要继续更新祖先结点的 _bf

如果插入结点在祖先结点的右子树,祖先结点的平衡因子从 0 -> 1
如果插入结点在祖先节点的左子树,祖先结点的平衡因子从 0 -> -1

无论是这两种的那种情况,对于更新后 _bf == -1 || _bf == 1 的结点的祖先结点而言,子树的高度都增加了 1
继续更新父结点

  • 当 _bf == -2 || _bf == 2 时,当前更新 _bf 的结点左右子树高度差超过 1 了,已经不平衡了,此时需要对该结点所在的子树进行旋转,旋转之后该结点的 _bf 会变成 0,此时也不用继续更新祖先结点的 _bf 了

旋转有四种情况:右单旋、左单旋、左单旋再右单旋、右单旋再左单旋
在这里插入图片描述

  • 右单旋:插入结点在较高左子树的左侧

在这里插入图片描述

  • 左单旋:插入结点在较高右子树的右侧,旋转方法类似于右单旋

  • 左单旋再右单旋:插入结点在较高左子树的右侧,旋转方法类似于右单旋再左单旋

  • 右单旋再左单旋:插入结点在较高右子树的左侧

在这里插入图片描述

// 右单旋
void RotateR(Node* parent)
{Node* pparent = parent->_parent;Node* subL = parent->_left;Node* subLR = subL->_right;parent->_left = subLR;if (subLR) subLR->_parent = parent;subL->_right = parent;parent->_parent = subL;if (pparent == nullptr) _root = subL;else{if (pparent->_kv.first > subL->_kv.first) pparent->_left = subL;else pparent->_right = subR;}subL->_parent = pparent;
}// 左单旋
void RotateL(Node* parent)
{Node* pparent = parent->_parent;Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;if (subRL) subRL->_parent = parent;subR->_left = parent;parent->_parent = subR;if (pparent == nullptr) _root = subR;else{if (pparent->_kv.first > subR->_kv.first) pparent->_left = subR;else pparent->_right = subR;}subR->_parent = pparent;
}// 插入
bool Insert(const std::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->_left;}else if (cur->_kv.first < kv.first){parent = cur;cur = cur->_right;}else return false;}cur = new Node(kv);if (parent->_kv > kv.first) parent->_left = cur;else parent->_right = cur;cur->_parent = parent;// 更新平衡因子while (parent){// 如果插入结点在祖先结点的左子树,_bf--// 如果插入结点在祖先结点的右子树,_bf++if (parent->_left == cur) parent->_bf--;else parent->_bf++;// 当 _bf == 0 时,结点所在的子树高度没有变化,不用继续更新祖先结点的 _bf// 当 _bf == -1 || _bf == 1 时,结点所在的子树高度增加 1,需要继续更新祖先结点的 _bf,最多更新到根结点// 当 _bf == -2 || _bf == 2 时,结点所在的子树不平衡了,需要对子树进行旋转,旋转之后 _bf 变为 0,也不用继续更新祖先结点的 _bf 了// 当 _bf 为其他值时,说明出大问题了if (parent->_bf == 0) break;else if (parent->_bf == -1 || parent->_bf == 1){// 继续更新parent = parent->_parent;cur = cur->_parent;}else if (parent->_bf == -2 || parent->_bf == 2){// 旋转// parent->_bf == -2 && cur->_bf == -1 右单旋// parent->_bf ==  2 && cur->_bf ==  1 左单旋// parent->_bf == -2 && cur->_bf ==  1 左单旋再右单旋// parent->_bf ==  2 && cur->_bf == -1 右单旋再左单旋// 当 _bf 为其他值时,说明出大问题了if (parent->_bf == -2 && cur->_bf == -1){RotateR(parent);parent->_bf = 0;cur->_bf = 0;}else if (parent->_bf == 2 && cur->_bf == 1){RotateL(parent);	parent->_bf = 0;cur->_bf = 0;}else if (parent->_bf == -2 && cur->_bf == 1){Node* sub = cur->_right;int bf = sub->_bf;RotateL(cur);RotateR(parent);// bf ==  0 sub 就是新增// bf == -1 sub 左边新增// bf ==  1 sub 右边新增sub->_bf = 0;if (bf == 0){parent->_bf = 0;cur->_bf = 0;}else if (bf == -1){parent->_bf = 1;cur->_bf = 0;}else if (_bf == 1){parent->_bf = 0;cur->_bf = -1;}else assert(false);}else if (parent->_bf == 2 && cur->_bf == -1){Node* sub = cur->_left;int bf = sub->_bf;RotateR(cur);RotateL(parent);// bf ==  0 sub 就是新增// bf == -1 sub 左边新增// bf ==  1 sub 右边新增sub->_bf = 0;if (bf == 0){parent->_bf = 0;cur->_bf = 0;}else if (bf == -1){parent->_bf = 0;cur->_bf = 1;}else if (bf == 1){parent->_bf = -1;cur->_bf = 0;}else assert(false);}else assert(false);break;}else assert(false);}return true;
}

相关文章:

AVL 树

文章目录 一、AVL 树的概念二、AVL 树的实现1. AVL 树的存储结构2. AVL 树的插入 一、AVL 树的概念 在 二叉搜索树 中&#xff0c;当我们连续插入有序的数据时&#xff0c;二叉搜索树可能会呈现单枝树的情况&#xff0c;此时二叉搜索树的查找效率为 O(N) 俄罗斯的两位数学家 …...

ggplot2做图(填坑中)

数据 df <- data.frame(x 1:10, y 1:10, f c(rep("A", 5), rep("B", 5))) 做图 1. 散点图 (scatter plot) # scatter plot scatter_plot <- function(df, metadata) {identical(rownames(df), rownames(metadata))data <- cbind(df, metada…...

Python日志处理器,同时打印到控制台和保存到文件中,并保证格式一致

使用logging模块的时候&#xff0c;默认是输出到控制台的&#xff0c;当然也可以配置输出到文件中&#xff0c;但是当你配置了文件后&#xff0c;控制台的输出就消失了&#xff0c;所以&#xff0c;需要一个策略即能保存到文件中&#xff0c;又能输出到控制台中。 下面是我做的…...

JavaWeb后端开发登录操作 登录功能 通用模板/SpringBoot整合

登录功能的思路 前端会传入两个参数:用户名和密码 在用户表中查询用户名,并校对相应的密码(涉及查询操作) SQL语句 select * from emp where username jingyong and password 123456; 如果有则成功,没有则登录失败.不可能为多个,因为添加了unique唯一约束,最终只会有一条 …...

The 2023 ICPC Asia Regionals Online Contest (1)(A D I J K L)

The 2023 ICPC Asia Regionals Online Contest (1)(A D I J K L) PTA | 程序设计类实验辅助教学平台 A Qualifiers Ranking Rules(模拟) 考虑先对第一场和第二场分别去重(取最好) &#xff0c; 归并排序后再次去重即可。 #include<bits/stdc.h> using namespace std;…...

C++ PrimerPlus 复习 第七章 函数——C++的编程模块(上)

第一章 命令编译链接文件 make文件 第二章 进入c 第三章 处理数据 第四章 复合类型 &#xff08;上&#xff09; 第四章 复合类型 &#xff08;下&#xff09; 第五章 循环和关系表达式 第六章 分支语句和逻辑运算符 第七章 函数——C的编程模块&#xff08;上&#xff…...

2.求循环小数

题目 对于任意的真分数 N/M &#xff08; 0 < N < M &#xff09;&#xff0c;均可以求出对应的小数。如果采用链表表示各个小数&#xff0c;对于循环节采用循环链表表示&#xff0c;则所有分数均可以表示为如下链表形式。 输入&#xff1a; N M 输出&#xff1a; 转换…...

zabbix监控告警邮箱提醒,钉钉提醒

一、注册网易邮箱及其配置邮箱 1、开启POP3/SMTP/IMAP 二、service端配置邮件服务 1.安装 mailx dos2unix yum install -y mailx dos2unix mailx&#xff1a;邮件服务 mos2unix&#xff1a;用于转换文本文件格式的实用工具 查看mailx版本 2.配置mailx配置文件 编辑&#xf…...

典型数据结构-栈/队列/链表、哈希查找、二叉树(BT)、线索二叉树、二叉排序树(BST树)、平衡二叉树(AVL树)、红黑树(RB树)

目录 典型数据结构列举 栈/队列/链表 树 二叉树 线索二叉树 二叉排序树 平衡二叉树&#xff08;AVL树&#xff09; 红黑树 其它树种和应用介绍 典型数据结构列举 栈/队列/链表 描述略。 一些基本的简单实现参考/数据结构简单实现/文件夹里面。 线性表详解&#xff…...

pyarmor 加密许可证的使用

一 pyarmor 许可证的用处 文档&#xff1a;5. 许可模式和许可证 — Pyarmor 8.3.6 文档 试用版本有如下的限制&#xff1a; 加密功能对脚本大小有限制&#xff0c;不能加密超过限制的大脚本。 混淆字符串功能在试用版中无法使用。 RFT 加密模式&#xff0c;BCC 加密模式在试…...

网络路径监控分析

不间断的连接应该是任何企业的首要任务。然而&#xff0c;确保网络中的源和目标之间持续、不间断的联系一直是网络通信中一个劳动密集型的过程。了解网络路径中的障碍、识别它们并迅速解决它们以维护健康、不间断的网络至关重要。 为什么要监控网络路径 维护网络运行状况是任…...

vue双向数据绑定是如何实现的?

Vue中的双向数据绑定主要是通过数据劫持和发布订阅模式来实现的。 数据劫持&#xff1a; Vue通过使用Object.defineProperty()方法来对data对象中的属性进行劫持&#xff0c;从而实现对数据的双向绑定。具体实现方式为&#xff1a; &#xff08;1&#xff09;在Vue实例化时&a…...

el-date-picker 封装一个简单的日期组件, 主要是禁用日期

子组件 <template><div><el-date-pickerv-model"dateModel"type"datetimerange":picker-options"pickerOptions"range-separator"至"ref"picker"start-placeholder"开始日期"end-placeholder&quo…...

保研复习-计算机组成原理

计算机组成原理 计算机组成冯诺依曼体系结构计算机系统的层次结构计算机的五大组成部件编译和解释的区别 CPUCPU的组成寄存器的类型指令类型指令功能指令执行过程 存储器存储器的层次结构寻址方式 输入和输出io方式有哪几种IO接口的基本结构 计算机组成 冯诺依曼体系结构 存储…...

linux环境安装redis(亲测完成)

linux环境安装redis 亲测完成 前言一、redis简介Redis 与其他 key - value 缓存产品有以下三个特点&#xff1a;Redis 优势 二、安装redis1.下载安装包2.创建服务器安装路径3.上传安装包4.解压安装包5.依赖安装6.编译 三、启动1)默认启动错误解决方式 2)指定配置启动2.1&#x…...

关于命令行交互自动化,及pyinstaller打包wexpect的问题

Python自动化工具 用来执行命令并进行交互&#xff0c;比如需要输入账号密码或者确认的场景 linux平台可以用pexpect&#xff0c;但是windows平台有一些差异&#xff0c;比较好用的是pexpect的变种wexpect&#xff0c;如果脚本中用了wexpect&#xff0c;并且要打包成onefile&a…...

8.4 【MySQL】文件系统对数据库的影响

因为 MySQL 的数据都是存在文件系统中的&#xff0c;就不得不受到文件系统的一些制约&#xff0c;这在数据库和表的命名、表的大小和性能方面体现的比较明显&#xff0c;比如下边这些方面&#xff1a; 数据库名称和表名称不得超过文件系统所允许的最大长度。 每个数据库都对应…...

Python WEB框架FastAPI (二)

Python WEB框架FastAPI &#xff08;二&#xff09; 最近一直在使用fastapi&#xff0c;随着使用的深入发现我对于它的了解还是太少了&#xff0c;以至于踩了一些坑。所以在这里记录一下&#xff0c;愿看到的小伙伴不迷路。 路径传参并发问题 一、路径传参 这是对上一个传参…...

基于Java网络书店商城设计实现(源码+lw+部署文档+讲解等)

博主介绍&#xff1a;✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专…...

怒刷LeetCode的第3天(Java版)

目录 第一题 题目来源 题目内容 解决方法 方法一&#xff1a;动态规划 第二题 题目来源 题目内容 解决方法 方法一&#xff1a;模拟 方法二&#xff1a;数学规律 方法三&#xff1a;分组 第三题 题目来源 题目内容 解决方法 方法一&#xff1a;数学方法 方法…...

JavaScript数组去重常用方法

数组去重是在 JavaScript 开发中经常遇到的问题。本文将从前言、分析、使用场景、具体实现代码和注意事项等方面&#xff0c;详细讨论 JavaScript 数组去重的方法。 前言&#xff1a; 在 JavaScript 中&#xff0c;数组是一种常用的数据结构&#xff0c;用于存储多个值。然而…...

蓝牙电话之HFP—电话音频

1 媒体音频&#xff1a; 播放蓝牙音乐的数据&#xff0c;这种音频对质量要求高&#xff0c;数据发送有重传机制&#xff0c;从而以l2cap的数据形式走ACL链路。编码方式有&#xff1a;SBC、AAC、APTX、APTX_HD、LDAC这五种编码方式&#xff0c;最基础的编码方式是SBC&#xff0…...

JDBC基本概念

什么是JDBC JDBC概念 JDBC&#xff08;Java DataBase Connectivity&#xff09;是一套统一的基于Java语言的关系数据库编程接口规范。 该规范允许将SQL语句作为参数通过JDBC接口发送给远端数据库&#xff0c; …...

leetcode876 链表的中间节点

题目 给你单链表的头结点 head &#xff0c;请你找出并返回链表的中间结点。 如果有两个中间结点&#xff0c;则返回第二个中间结点。 示例 输入&#xff1a;head [1,2,3,4,5] 输出&#xff1a;[3,4,5] 解释&#xff1a;链表只有一个中间结点&#xff0c;值为 3 。 输入&a…...

了解方法重写

父类 package com.mypackage.oop.demo07;//重写都是方法的重写&#xff0c;与属性无关 public class B {public static void test(){System.out.println("B>test.()");}public void test2(){System.out.println("B2>test.()");} }子类 package com…...

2、从“键鼠套装”到“全键盘游戏化”审核

1、风行内容仓的增效之路 - 前言 内容仓成立初期&#xff0c;安全审核组是规模最大的小组&#xff0c;占到部门人数的半壁江山&#xff0c;因此增效工作首先就从安全审核开始。 早期安全审核每天的达标值在800条左右&#xff0c;每天的总审核量不到1万&#xff0c;距离业务部门…...

【flutter】架构之商城main入口

架构之商城main入口 前言一、项目模块的划分二、入口main的配置三、配置文件怎么做总结 前言 本栏目我们将完成一个商城项目的架构搭建&#xff0c;并完善中间的所有功能&#xff0c;总页面大概200个&#xff0c;如果你能看完整个栏目&#xff0c;你肯定能独立完成flutter 项目…...

linux学习实操计划0103-安装软件

本系列内容全部给基于Ubuntu操作系统。 系统版本&#xff1a;#32~22.04.1-Ubuntu SMP PREEMPT_DYNAMIC Fri Aug 18 10:40:13 UTC 1 安装deb格式软件 Debian包是Unixar的标准归档&#xff0c;将包文件信息以及包内容&#xff0c;经过gzip和tar打包而成。 处理这些包的经典程序是…...

git vscode

01&#xff1a;工作区 **02&#xff1a;暂存区 git add . 3&#xff1a;本地库 git commit -m ’ 4&#xff1a;远程库 git push example 点击箭头之后...

Linux命令行批量删除文件

1、 删除当前目录下60min前的所有.log结尾文件 find ./ -type f -name "*.log" -mmin 60 -delete 2、删除当前目录下30天前的所有.log结尾文件 find ./ -type f -name "*.log" -mtime 30 -delete...

免费的网站推广 外贸/网络优化的三个方法

php Undefined index和Undefined variable的解决方法$act$_POST[act];用以上代码总是提示Notice: Undefined index: act in F:\windsflybook\post.php on line 18另外&#xff0c;有时还会出现引用内容Notice: Undefined variable: Submit ......等一些这样的提示原因&#xff…...

济宁建设局官方网站/seo技术培训班

问题求解1&#xff1a; 从一个 44 的棋盘&#xff08;不可旋转&#xff09;中选取不在同一行也不在同一列上的两个方格&#xff0c;共有____72_____种方法。 假设选择第一行&#xff0c;共有4个格子可以选择&#xff0c;然后从剩余的3行中进行选择&#xff0c;有4X3种可能。…...

针对网站做搜索引擎做优化/重庆seo网络优化咨询热线

java.lang.StackOverflowError: null springboot 错误原因&#xff1a;service层 自动装配mapper错误 装配成本service了&#xff0c; 出现这个问题的原因是由于深度递归&#xff0c;抛出此错误以指示应用程序的堆栈已耗尽。...

有关天猫网站建设的论文/在什么网站可以免费

java项目转maven项目&#xff0c;要注意pom.xml文件中是否定义了JDK的版本&#xff0c;要与环境保持一致。项目&#xff0c;右键&#xff0c;configure&#xff0c;选择转换为maven项目即可。转换后&#xff0c;有三个位置需要注意&#xff1a;1、Java compiler 编译级别2、…...

网站的注册页面怎么做/安卓手机优化神器

软件工程——理论、方法与实践 之 软件演化 一旦软件进去使用期&#xff0c;一些暴露出来的软件问题需要进一步修正和改善&#xff0c;同时新的需求会不断出现&#xff0c;原有的需求也会随着业务的变化而发生变化。因此&#xff0c;软件系统在交付之后仍然在不断地演化&#x…...

阿里云空间可以做网站吗/外贸网站免费推广

一、诈骗罪的起刑点以及量刑是怎么规定的 1、诈骗罪起刑点为诈骗的公私财物价值达到三千元。 2、诈骗罪的量刑标准如下所述&#xff1a; &#xff08;1&#xff09;数额较大的&#xff0c;处三年以下有期徒刑、拘役或者管制&#xff0c;并处或者单处罚金; &#xff08;2&am…...