【C++ —— AVL树】
C++ —— AVL树
- AVL树的概念
- AVL树节点的定义
- AVL树的插入
- 向上调整
- 旋转
- 左单旋
- 右单旋
- 左右双旋
- 右左双旋
- AVL树的高度
- AVL树的验证
- 总结:
- 代码
AVL树的概念
二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下。因此,两位俄罗斯的数学家G.M.Adelson-Velskii和E.M.Landis在1962年发明了一种解决上述问题的方法: 当向二叉搜索树中插入新结点后,如果能保 证每个结点的左右子树高度之差的绝对值不超过1(需要对树中的结点进行调整) 即可降低树的高度,从而减少平均搜索长度。
AVL树本质上是一颗二叉查找树,但是它又具有以下特点:
- 它的左右子树都是AVL树
- 左右子树高度之差(简称平衡因子)的绝对值不超过1 即(-1 / 0 / 1)。
在AVL树中,任何节点的两个子树的高度最大差别为 1 ,所以它也被称为平衡二叉树.
如果一棵二叉搜索树是高度平衡的,它就是AVL树。如果它有n个结点,其高度可保持在 o ( l o g 2 n ) o(log_2 n) o(log2n),搜素时间复杂度o( l o g 2 n log_2 n log2n)。

本文规定:
平衡因子时采用公式:平衡因子 = 右子树高度 - 左子树高度
AVL树节点的定义
template<class K,class V>
struct AVLTreeNode
{AVLTreeNode<K, V>* _left;AVLTreeNode<K, V>* _right;AVLTreeNode<K, V>* _parent;pair<K, V> _kv;int _bf; //存储当前节点的平衡因子//构造函数AVLTreeNode(const pair<K, V>& kv):_left(nullptr),_right(nullptr),_parent(nullptr),_kv(kv),_bf(0){}
};
这里使用了pair来存储节点的值,_bf表示改节点的平衡因子。
AVL树的插入
AVL树的插入步骤大致分为下面两种
- 找到准备插入节点的位置
- 更新节点的平衡因子
- 做出相应的调整
看下面几个实例:


因为新插入的节点会影响到树原本的平衡,所以每次插入都得更新节点的平衡因子,平衡因子的变化有以下几种:
- 首先按照搜索树的规则插入
- 更新插入节点的祖先节点的平衡因子
- 插入父亲的左边,父亲平衡因子
- - - 插入父亲的右边,父亲平衡因子
+ +
- 插入父亲的左边,父亲平衡因子
- 根据父亲的平衡因子分以下三种调整:
- 父亲平衡因子是 0 , 父亲所在子树的高度不变,稳定,不需要向上更新。
- 父亲平衡因子是 1 / -1 ,父亲所在子树的高度变了,继续向上更新直到稳定。
- 父亲平衡因子是 2 / -2 ,父亲所在子树已经不平衡了,需要旋转处理。

向上调整
while (parent){if (cur == parent->_left){parent->_bf--;}else{parent->_bf++;}if (parent->_bf == 0) //已经平衡,无需调整,结束。{break;}else if (parent->_bf == 1 || parent->_bf == -1) // 0 -> 1 / -1 需要线上调整{cur = parent;parent = cur->_parent;}else if (parent->_bf == 2 || parent->_bf == -2) //1 / -1 -> 2 -2 需要旋转{//旋转}}
旋转
AVL树的旋转分为两种:单旋 和 双旋
其中单旋转又分为:右单旋(RotateR) 和 左单旋(RotateL)
其中双旋转又分为:右左双旋(RotateRL) 和 左右双旋(RotateLR)
为什么会有多旋呢?
因为有些情况单旋无法解决,不得不选择双旋,具体情况下文将继续讨论。
左单旋
假设我们有下图AVL树:

现在需要插入新节点15,插入后如图:

但因为此时节点13的平衡因子为2,不稳定,需要旋转处理,如下图:

由结果可知,将节点13进行左旋转,让节点13的父亲变为节点14,节点14的左孩子变为节点13,节点14的父亲变为节点12,即可达到平衡。
动图演示:

代码:
void RotateL(Node* parent){Node* SubR = parent->_right;Node* SubRL = SubR->_left;parent->_right = SubRL;if (SubRL){SubRL->_parent = parent;}Node* ppNode = parent->_parent;SubR->_left = 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 = SubR->_bf = 0;}
右单旋
假设我们有下图AVL树:

现在需要插入新节点3,插入后如图:

但因为此时节点5的平衡因子为**-2**,不稳定,需要旋转处理,如下图:

由结果可知,将节点5进行右旋转,让节点5的父亲变为节点4,节点4的右孩子变为节点5,节点4的父亲变为节点10,即可达到平衡。
动图演示:

void RotateR(Node* parent){Node* SubL = parent->_left;Node* SubLR = SubL->_right;parent->_left = SubLR;if (SubLR){SubLR->_parent = parent;}Node* ppNode = parent->_parent;SubL->_right = 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;}parent->_bf = SubL->_bf = 0;}
左右双旋
假设我们有下图AVL树:

现在需要插入新节点7,插入后如图:

但因为此时节点10的平衡因子为 -2 节点4的平衡因子是1 ,不稳定,需要先进行左旋处理,如下图:

左旋完成后,此时节点10的平衡因子仍为 -2 不稳定,再进行右单旋,如下图:

由结果可知,经过节点4的左旋处理,再经过节点10的右旋处理,最终平衡。
动图演示:

由上述的上述调整代码可知,因为节点10的平衡因子是-2,所以在进行左右双旋时,节点10是parent,我们定义 节点5为SubL,节点4为SubLR。分别对SubL和parent进行左单旋和右单旋。
但是上述是吧新添加的节点插入到了5的右边,实际上共有下面三中情况可以引起树的双旋转:
1. 插入在较高左子树的右侧的左边
2. 插入在较高左子树的右侧的右边
3. 直接当作较高左子树的右边
插入在较高左子树的右侧的左边:

2. 插入在较高左子树的右侧的右边

3. 直接当作较高左子树的右边

所以由上面三种情况 ,我们可以总结出最终的平衡因子调整规则:
- 当
subLR = 0时:调整为subLR = 0 ,subL = 0 ,parent= 0 - 当
subLR =-1时:调整为subLR =0 ,subL = 0 ,parent = 1 - 当
subLR = 1时:调整为subLR = 0 ,subL =-1,parent = 0
代码:
void RotateLR(Node* parent){Node* SubL = parent->_left;Node* SubLR = SubL->_right;int bf = SubLR->_bf;RotateL(SubL);xRotateR(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);}}
右左双旋
由左右双旋的推理我们可以得出: 当将新添加的节点插入到较高右子树的左侧使需要右左双旋。
具体分为三种情况:
1. 插入在较高右子树的左侧的右边
2. 插入在较高右子树的左侧的左边
3. 直接当作较高右子树的左边
1. 插入在较高右子树的左侧的右边

2. 插入在较高右子树的左侧的左边

3. 直接当作较高右子树的左边

代码:
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){SubRL->_bf = 0;parent->_bf = -1;SubR->_bf = 0;}else if (bf == -1){SubRL->_bf = 0;parent->_bf = 0;SubR->_bf = 1;}else{SubRL->_bf = 0;parent->_bf = 0;SubR->_bf = 0;}}
AVL树的高度
int _Height(Node* root){if (root == nullptr)return 0;return max(_Height(root->_left), _Height(root->_right)) + 1;}
AVL树的验证
bool _IsBalance(Node* root){if (root == nullptr)return true;int leftHeight = _Height(root->_left);int rightHeight = _Height(root->_right);// 不平衡if (abs(leftHeight - rightHeight) >= 2){cout << root->_kv.first << endl;return false;}// 顺便检查一下平衡因子是否正确if (rightHeight - leftHeight != root->_bf){cout << root->_kv.first << endl;return false;}return _IsBalance(root->_left)&& _IsBalance(root->_right);}
总结:

代码
#pragma once
#include <assert.h>
#include <vector>
#include <iostream>using namespace std;
template<class K,class V>
struct AVLTreeNode
{AVLTreeNode<K, V>* _left;AVLTreeNode<K, V>* _right;AVLTreeNode<K, V>* _parent;pair<K, V> _kv;int _bf; //存储当前节点的平衡因子//构造函数AVLTreeNode(const 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 pair<K, V>& kv){if (_root == nullptr){_root = new Node(kv);return true;}Node* parent = nullptr;Node* cur = _root;while (cur){if (kv.first > cur->_kv.first){parent = cur;cur = cur->_right;}else if (kv.first < cur->_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;//更新平衡因子while (parent){if (cur == parent->_left){parent->_bf--;}else{parent->_bf++;}if (parent->_bf == 0) //已经平衡,无需调整,结束。{break;}else if (parent->_bf == 1 || parent->_bf == -1) // 0 -> 1 / -1 需要线上调整{cur = parent;parent = cur->_parent;}else if (parent->_bf == 2 || parent->_bf == -2) //1 / -1 -> 2 -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){RotateRL(parent);}else if (parent->_bf == -2 && cur->_bf == 1){RotateLR(parent);}break;}else{//不会出现的情况assert(false);}}return true;}Node* Find(const K& key){Node* cur = _root;while (cur){if (key > cur->_kv.first){cur = cur->_right;}else if (key < cur->_kv.first){cur = cur->_left;}else{return cur;}}return nullptr;}void RotateR(Node* parent){Node* SubL = parent->_left;Node* SubLR = SubL->_right;parent->_left = SubLR;if (SubLR){SubLR->_parent = parent;}Node* ppNode = parent->_parent;SubL->_right = 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;}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;}Node* ppNode = parent->_parent;SubR->_left = 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 = SubR->_bf = 0;}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){SubRL->_bf = 0;parent->_bf = -1;SubR->_bf = 0;}else if (bf == -1){SubRL->_bf = 0;parent->_bf = 0;SubR->_bf = 1;}else{SubRL->_bf = 0;parent->_bf = 0;SubR->_bf = 0;}}void RotateLR(Node* parent){Node* SubL = parent->_left;Node* SubLR = SubL->_right;int bf = SubLR->_bf;RotateL(SubL);xRotateR(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);}}bool IsBalance(){return _IsBalance(_root);}int Height(){return _Height(_root);}int Size(){return _Size(_root);}void InOrder(){_InOrder(_root);cout << endl;}private:int _Size(Node* root){return root == nullptr ? 0 : _Size(root->_left) + _Size(root->_right) + 1;}int _Height(Node* root){if (root == nullptr)return 0;return max(_Height(root->_left), _Height(root->_right)) + 1;}bool _IsBalance(Node* root){if (root == nullptr)return true;int leftHeight = _Height(root->_left);int rightHeight = _Height(root->_right);// 不平衡if (abs(leftHeight - rightHeight) >= 2){cout << root->_kv.first << endl;return false;}// 顺便检查一下平衡因子是否正确if (rightHeight - leftHeight != root->_bf){cout << root->_kv.first << endl;return false;}return _IsBalance(root->_left)&& _IsBalance(root->_right);}void _InOrder(Node* root){if (root == nullptr){return;}_InOrder(root->_left);cout << root->_kv.first << ":" << root->_kv.second << endl;_InOrder(root->_right);}Node* _root = nullptr;
};void TestAVLTree1()
{//int a[] = { 8, 3, 1, 10, 6, 4, 7, 14, 13 };int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };AVLTree<int, int> t1;for (auto e : a){/*if (e == 4){int i = 0;}*/// 1、先看是插入谁导致出现的问题// 2、打条件断点,画出插入前的树// 3、单步跟踪,对比图一一分析细节原因t1.Insert({ e,e });cout << "Insert:" << e << "->" << t1.IsBalance() << endl;}t1.InOrder();cout << t1.IsBalance() << endl;
}void TestAVLTree2()
{const int N = 100000;vector<int> v;v.reserve(N);srand(time(0));for (size_t i = 0; i < N; i++){v.push_back(rand() + i);//cout << v.back() << endl;}size_t begin2 = clock();AVLTree<int, int> t;for (auto e : v){t.Insert(make_pair(e, e));//cout << "Insert:" << e << "->" << t.IsBalance() << endl;}size_t end2 = clock();cout << "Insert:" << end2 - begin2 << endl;//cout << t.IsBalance() << endl;cout << "Height:" << t.Height() << endl;cout << "Size:" << t.Size() << endl;size_t begin1 = clock();// 确定在的值for (auto e : v){t.Find(e);}// 随机值/*for (size_t i = 0; i < N; i++){t.Find((rand() + i));}*/size_t end1 = clock();cout << "Find:" << end1 - begin1 << endl;
}
相关文章:
【C++ —— AVL树】
C —— AVL树 AVL树的概念AVL树节点的定义AVL树的插入向上调整旋转左单旋右单旋左右双旋右左双旋 AVL树的高度AVL树的验证总结:代码 AVL树的概念 二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素…...
跨平台webSocket模块设计技术解决方案
1. 概述 目标:设计并实现一个能够在多种操作系统上运行的WebSocket通讯模块,支持与前端浏览器和HTTPS服务端进行数据交换。技术栈:C11 ,使用跨平台库如 Boost处理网络IO,使用 JSON 库如 nlohmann/json 解析消息。 2.…...
Drools规则引擎
一、Drools规则引擎 Drools官网: https://www.drools.org/Drools中文网: http://www.drools.org.cn/bilibili学习视频(黑马博学谷2020年最新Java项目Drools业务规则管理系统(BRMS)): https://www.bilibili.com/video/BV1Pa4y1a7u…...
vue学习day11-路由、路由模块的封装、声明式导航-路由的介绍、VueRouter、router-link、自定义高亮类名
32、路由 (1)路由的介绍 1)生活中的路由:设备和ip的映射关系 2)路由:一种映射关系 3)Vue中的路由:路径与组件的映射关系 (根据路由就能知道不同的路径,应…...
智慧校园学期基础数据管理
在智慧校园基础数据管理之一的学期管理功能管理中,学期的有序管理具有重要意义。它不仅是教学活动有序开展的指挥棒,更是连接学校管理者、教师与学生之间沟通的桥梁,承载着规划、跟踪与管理学期内各项事务的重要使命。 学期管理功能的首要任务…...
ISP代理和双ISP代理:区别和优势
随着互联网技术的不断发展和普及,网络代理服务成为众多用户保护隐私、提高网络性能、增强安全性的重要工具。其中,ISP代理和双ISP代理是两种常见的网络代理服务形式。本文将详细探讨ISP代理和双ISP代理的区别和优势,以便用户更好地了解并选择…...
【雷丰阳-谷粒商城 】【分布式高级篇-微服务架构篇】【22】【RabbitMQ】
持续学习&持续更新中… 守破离 【雷丰阳-谷粒商城 】【分布式高级篇-微服务架构篇】【22】【RabbitMQ】 Message Queue 消息队列异步处理应用解耦流量控制 消息中间件概念RabbitMQ概念MessagePublisherExchangeQueueBindingConnectionChannelConsumerVirtual HostBroker图…...
概率论原理精解【4】
文章目录 度量空间概述理论基础定义特点高级概念广泛应用 性质例子应用 柯西数列柯西数列的定义柯西数列的例子 参考文献 度量空间 概述 设 f : R n → R m , f ˙ ( x ) 在 { x : ∣ x − x 0 ∣ < r } 内连续,则当 ∣ t ∣ < r 时, f:R^n\righ…...
Linux云计算 |【第一阶段】ENGINEER-DAY3
主要内容: LVM逻辑卷管理、VDO、RAID磁盘阵列、进程管理 一、新建逻辑卷 1、什么是逻辑卷 逻辑卷(Logical Volume)是逻辑卷管理(Logical Volume Management,LVM)系统中的一个概念。LVM是一种用于磁盘管理…...
springboot 实体类加注解校验入参数据
导入的是springboot自身的依赖包 import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import javax.validation.Valid;...
关于 Qt输入法在arm特定的某些weston下出现调用崩溃 的解决方法
若该文为原创文章,转载请注明原文出处 本文章博客地址:https://hpzwl.blog.csdn.net/article/details/140423667 长沙红胖子Qt(长沙创微智科)博文大全:开发技术集合(包含Qt实用技术、树莓派、三维、OpenCV…...
Android Studio关于Gradle及JDK问题解决
1.Android Studio 版本如:Android Studio Koala | 2024.1.1 2.Gradle 版本为:8.7 3.JDK 版本为:17 以上这三个必须匹配,具体可以看官网Android Studio 版本说明(https://developer.android.google.cn/studio?hlzh-…...
Leetcode 205. 同构字符串
205. 同构字符串 Leetcode 205. 同构字符串 一、题目描述二、我的想法三、其他人的题解 一、题目描述 给定两个字符串 s 和 t ,判断它们是否是同构的。 如果 s 中的字符可以按某种映射关系替换得到 t ,那么这两个字符串是同构的。 每个出现的字符都应…...
多口适配器,给您的生活增添便利
随着科技的快速发展,我们的生活已离不开各种各样的电子设备,智能手机、平板电脑、智能手表、无线耳机……它们共同构建了我们丰富多彩的数字生活。然而,面对众多设备的充电需求,传统的单一充电口已难以满足现代人的使用习惯。在这…...
探索现代Web开发:WebKit的剪贴板API革新
探索现代Web开发:WebKit的剪贴板API革新 在当今的Web开发领域,用户体验的提升是开发者们不懈追求的目标。其中一个关键的交互点便是剪贴板操作,它允许用户在网页与本地系统之间复制和粘贴数据。WebKit,作为Safari、QQ浏览器等众多…...
【电路笔记】-放大器的频率响应
放大器的频率响应 文章目录 放大器的频率响应1、概述2、定义3、电容器的影响4、低频响应5、高频响应6、总结1、概述 对于任何电子电路来说,放大器的行为都会受到其输入端子上信号频率的影响。 该特性称为频率响应。 频率响应是放大器最重要的特性之一。 在放大器设计的频率范…...
Artix7系列FPGA实现SDI视频编解码,基于GTP高速接口,提供3套工程源码和技术支持
目录 1、前言工程概述免责声明 2、相关方案推荐本博已有的 SDI 编解码方案本方案在Xilinx--Kintex系列FPGA上的应用本方案在Xilinx--Zynq系列FPGA上的应用 3、详细设计方案设计原理框图SDI 输入设备Gv8601a 均衡器GTP 高速接口-->解串与串化SMPTE SD/HD/3G SDI IP核BT1120转…...
NET 语言识别,语音控制操作、语音播报
System.Speech. 》》System.Speech.Synthesis; 语音播报 》》System.Speech.Recognition 语音识别 using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Speech.Recog…...
Unity 调试死循环程序
如果游戏出现死循环如何调试呢。 测试脚本 我们来做一个测试。 首先写一个死循环代码: using System.Collections; using System.Collections.Generic; using UnityEngine;public class dead : MonoBehaviour {void Start(){while (true){int a 1;}}}Unity对象设…...
视频监控平台LntonCVS视频融合共享平台智慧安防视频监控汇聚应用方案
LntonCVS是一款功能强大且灵活部署的安防视频监控平台。它支持多种主流标准协议,包括GB28181、RTSP/Onvif、RTMP等,同时能够兼容海康Ehome、海大宇等厂家的私有协议和SDK接入。该平台不仅提供传统的安防监控功能,还支持接入AI智能分析&#x…...
在软件开发中正确使用MySQL日期时间类型的深度解析
在日常软件开发场景中,时间信息的存储是底层且核心的需求。从金融交易的精确记账时间、用户操作的行为日志,到供应链系统的物流节点时间戳,时间数据的准确性直接决定业务逻辑的可靠性。MySQL作为主流关系型数据库,其日期时间类型的…...
【杂谈】-递归进化:人工智能的自我改进与监管挑战
递归进化:人工智能的自我改进与监管挑战 文章目录 递归进化:人工智能的自我改进与监管挑战1、自我改进型人工智能的崛起2、人工智能如何挑战人类监管?3、确保人工智能受控的策略4、人类在人工智能发展中的角色5、平衡自主性与控制力6、总结与…...
java 实现excel文件转pdf | 无水印 | 无限制
文章目录 目录 文章目录 前言 1.项目远程仓库配置 2.pom文件引入相关依赖 3.代码破解 二、Excel转PDF 1.代码实现 2.Aspose.License.xml 授权文件 总结 前言 java处理excel转pdf一直没找到什么好用的免费jar包工具,自己手写的难度,恐怕高级程序员花费一年的事件,也…...
2021-03-15 iview一些问题
1.iview 在使用tree组件时,发现没有set类的方法,只有get,那么要改变tree值,只能遍历treeData,递归修改treeData的checked,发现无法更改,原因在于check模式下,子元素的勾选状态跟父节…...
MySQL 8.0 OCP 英文题库解析(十三)
Oracle 为庆祝 MySQL 30 周年,截止到 2025.07.31 之前。所有人均可以免费考取原价245美元的MySQL OCP 认证。 从今天开始,将英文题库免费公布出来,并进行解析,帮助大家在一个月之内轻松通过OCP认证。 本期公布试题111~120 试题1…...
Aspose.PDF 限制绕过方案:Java 字节码技术实战分享(仅供学习)
Aspose.PDF 限制绕过方案:Java 字节码技术实战分享(仅供学习) 一、Aspose.PDF 简介二、说明(⚠️仅供学习与研究使用)三、技术流程总览四、准备工作1. 下载 Jar 包2. Maven 项目依赖配置 五、字节码修改实现代码&#…...
【VLNs篇】07:NavRL—在动态环境中学习安全飞行
项目内容论文标题NavRL: 在动态环境中学习安全飞行 (NavRL: Learning Safe Flight in Dynamic Environments)核心问题解决无人机在包含静态和动态障碍物的复杂环境中进行安全、高效自主导航的挑战,克服传统方法和现有强化学习方法的局限性。核心算法基于近端策略优化…...
【分享】推荐一些办公小工具
1、PDF 在线转换 https://smallpdf.com/cn/pdf-tools 推荐理由:大部分的转换软件需要收费,要么功能不齐全,而开会员又用不了几次浪费钱,借用别人的又不安全。 这个网站它不需要登录或下载安装。而且提供的免费功能就能满足日常…...
MySQL 索引底层结构揭秘:B-Tree 与 B+Tree 的区别与应用
文章目录 一、背景知识:什么是 B-Tree 和 BTree? B-Tree(平衡多路查找树) BTree(B-Tree 的变种) 二、结构对比:一张图看懂 三、为什么 MySQL InnoDB 选择 BTree? 1. 范围查询更快 2…...
适应性Java用于现代 API:REST、GraphQL 和事件驱动
在快速发展的软件开发领域,REST、GraphQL 和事件驱动架构等新的 API 标准对于构建可扩展、高效的系统至关重要。Java 在现代 API 方面以其在企业应用中的稳定性而闻名,不断适应这些现代范式的需求。随着不断发展的生态系统,Java 在现代 API 方…...
