【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…...
网络六边形受到攻击
大家读完觉得有帮助记得关注和点赞!!! 抽象 现代智能交通系统 (ITS) 的一个关键要求是能够以安全、可靠和匿名的方式从互联车辆和移动设备收集地理参考数据。Nexagon 协议建立在 IETF 定位器/ID 分离协议 (…...
【第二十一章 SDIO接口(SDIO)】
第二十一章 SDIO接口 目录 第二十一章 SDIO接口(SDIO) 1 SDIO 主要功能 2 SDIO 总线拓扑 3 SDIO 功能描述 3.1 SDIO 适配器 3.2 SDIOAHB 接口 4 卡功能描述 4.1 卡识别模式 4.2 卡复位 4.3 操作电压范围确认 4.4 卡识别过程 4.5 写数据块 4.6 读数据块 4.7 数据流…...
dedecms 织梦自定义表单留言增加ajax验证码功能
增加ajax功能模块,用户不点击提交按钮,只要输入框失去焦点,就会提前提示验证码是否正确。 一,模板上增加验证码 <input name"vdcode"id"vdcode" placeholder"请输入验证码" type"text&quo…...
TRS收益互换:跨境资本流动的金融创新工具与系统化解决方案
一、TRS收益互换的本质与业务逻辑 (一)概念解析 TRS(Total Return Swap)收益互换是一种金融衍生工具,指交易双方约定在未来一定期限内,基于特定资产或指数的表现进行现金流交换的协议。其核心特征包括&am…...
土地利用/土地覆盖遥感解译与基于CLUE模型未来变化情景预测;从基础到高级,涵盖ArcGIS数据处理、ENVI遥感解译与CLUE模型情景模拟等
🔍 土地利用/土地覆盖数据是生态、环境和气象等诸多领域模型的关键输入参数。通过遥感影像解译技术,可以精准获取历史或当前任何一个区域的土地利用/土地覆盖情况。这些数据不仅能够用于评估区域生态环境的变化趋势,还能有效评价重大生态工程…...
CMake 从 GitHub 下载第三方库并使用
有时我们希望直接使用 GitHub 上的开源库,而不想手动下载、编译和安装。 可以利用 CMake 提供的 FetchContent 模块来实现自动下载、构建和链接第三方库。 FetchContent 命令官方文档✅ 示例代码 我们将以 fmt 这个流行的格式化库为例,演示如何: 使用 FetchContent 从 GitH…...
深入解析C++中的extern关键字:跨文件共享变量与函数的终极指南
🚀 C extern 关键字深度解析:跨文件编程的终极指南 📅 更新时间:2025年6月5日 🏷️ 标签:C | extern关键字 | 多文件编程 | 链接与声明 | 现代C 文章目录 前言🔥一、extern 是什么?&…...
【开发技术】.Net使用FFmpeg视频特定帧上绘制内容
目录 一、目的 二、解决方案 2.1 什么是FFmpeg 2.2 FFmpeg主要功能 2.3 使用Xabe.FFmpeg调用FFmpeg功能 2.4 使用 FFmpeg 的 drawbox 滤镜来绘制 ROI 三、总结 一、目的 当前市场上有很多目标检测智能识别的相关算法,当前调用一个医疗行业的AI识别算法后返回…...
《C++ 模板》
目录 函数模板 类模板 非类型模板参数 模板特化 函数模板特化 类模板的特化 模板,就像一个模具,里面可以将不同类型的材料做成一个形状,其分为函数模板和类模板。 函数模板 函数模板可以简化函数重载的代码。格式:templa…...
VM虚拟机网络配置(ubuntu24桥接模式):配置静态IP
编辑-虚拟网络编辑器-更改设置 选择桥接模式,然后找到相应的网卡(可以查看自己本机的网络连接) windows连接的网络点击查看属性 编辑虚拟机设置更改网络配置,选择刚才配置的桥接模式 静态ip设置: 我用的ubuntu24桌…...
