【AVL树】—— 我与C++的不解之缘(二十三)
什么是AVL树?
AVL树发明者是G. M. Adelson-Velsky和E. M. Landis两个前苏联科学家,他们在1962年论文《An algorithm for the organization of information》中发表了AVL树。AVL树是最先发明的自平衡二叉搜索树,说白了就是能够自己控制平衡结构的一个二叉搜索树;AVL可以是一个空树,或者其左右树都是AVL树,且左右子树的高度差的绝对值不超过1。AVL树,左右子树的高度差不超过一,而不是0?(如果一棵树的节点个数是2、4等的情况下,高度差最好情况就是1,到不到0。- 本篇在实现
AVL树时,引入了一个新的概念(平衡因子);每个节点都存在平衡因子,平衡因子等于右子树的高度减去左子树的高度,这样平衡因子的取值就是(0、1、-1);(平衡因子也不是必须的,这里引入平衡因子这一概念,方便观察和控制整个AVL树的平衡。
简单来说,AVL树就是一个特殊的搜索二叉树,特殊就特殊在它可以控制平衡,保持左右子树的高度差不超过1。
那又是如何实现的呢?
AVL树的实现
1. AVL树的结构
先来看一下
AVL树的结构,首先就是AVL树的节点
template<class K,class V>
struct TreeNode {int _bf;pair<K, V> _kv;TreeNode<K, V>* _left;TreeNode<K, V>* _right;TreeNode<K, V>* _parent;TreeNode(const pair<K, V> kv):_kv(kv), _bf(0), _left(nullptr), _right(nullptr), _parent(nullptr){}
};
这里并没有直接存储数据K,和V,而是像map那样将其封装成一个pair<K,V>类型。
再来看一下
AVL树都要实现哪些方法
template<class K,class V>
class AVLTree
{typedef TreeNode<K, V> Node;
public://插入bool insert(const pair<K, V> kv) {}//查找bool find(const K& kev) {}//中序遍历void order() {}
private://右单旋void RevoleR(Node* parent) {}//左单旋void RevoleL(Node* parent) {}//右左双选void RevoleRL(Node* parent) {}//左右双选void RevoleLR(Node* parent) {}//中序遍历void order(Node* root) {}Node* _root;
};
这里实现了几个私有的成员方法,因为这些方法不希望在类外被直接访问。(其中order()是为了实现中序遍历,因为在类外无法访问到该树的根节点。)
2. AVL树的插入
插入过程
对于插入数据的整个过程,其实就是在搜索二叉树的基础上,增加了更新平衡因子和在更新平衡因子的过程中需要旋转的情况就行旋转。
- 按搜索二叉树的规则进行插入数据
- 新增节点以后,就可能会影响到部分祖先节点的平衡因子,所以从新增节点 -> 根节点这整个路径上节点的平衡因子(在更新的过程中,可能会遇到继续更新,更新结束以及需要旋转的情况。)
- 更新平衡因子过程中没有出现问题,插入就结束了。
- 在平衡的过程中,出现了不平衡的情况,就要堆不平衡子树进行旋转,选择后调平衡的同时,也降低了子树的高度,就不会影响上一层的平衡因子,插入也就结束了。
更新平衡因子
首先,平衡因子=右子树高度-左子树高度
- 插入节点会增加高度,所以,新增节点如果是在
parent节点的右子树,则parent节点的平衡因子++;如果是在parent节点的左子树,那么parent节点的平衡因子–;parent所在子树的高度是否变化就决定了是否要继续往上更新平衡因子。
更新平衡因子可能遇到的情况:
- 更新之后
parent节点平衡因子等于0:更新过程中parent的平衡因子变化-1->0或者1->0,这说明了插入节点之前parent子树一边高一边低,新增节点插入到了低的那一边,插入节点后以parent为根节点的子树的高度不变,就不会影响其父节点的平衡因子(就不会影响到上面节点的平衡)所以更新就结束了。- 更新之后
parent节点平衡因子等于1或-1:更新过程中parent的平衡因子变化0->-1或者0->1,这就说明了,插入节点之前,parent的左右子树高度相同了,插入节点之后parent子树的高度发生了变化,所以就会影响其父节点的平衡因子,从而影响上面节点的平衡;所以需要继续更新平衡因子。- 更新之后
parent节点平衡因子等于2或者-2:更新过程中parent的平衡因子变化1->2或者-1->-2,这说明,在插入节点之前,以parent为根节点的子树就已经一边高一边低了;然后新增节点还插入到了高的那一边,这样以parent为根节点的子树就已经不满足AVL树的结构了,此时就需要对该树就行旋转(旋转:一是将以parent为根节点的子树调整平衡,二是降低以parent为根节点的子树的高度,回复到插入以前的高度);旋转完成后,就不需要继续更新平衡因子了。
更新之后parent节点平衡因子为0

更新之后parent节点平衡因子为1或者-1

更新之后parent节点平衡因子为2或者-2

更新平衡因子的过程实现
bool insert(const pair<K, V> kv)
{Node* newnode = Node(kv);if (_root == nullptr){_root = newnode;return true;}Node* parent = nullptr;Node* pcur = _root;while (pcur){if (kv.first > pcur->_kv.first){parent = pcur;pcur = pcur->_right;}else if (kv.first < pcur->_kv.first){parent = pcur;pcur = pcur->_left;}else{return false;}}pcur = newnode;newnode->_parent = parent;if (kv.first > parent->_kv.first){parent->_right = pcur;}else if (kv.first < parent->_kv.first){parent->_left = pcur;}else{return false;}//更新平衡因子while (parent){if (pcur == parent->_left){--parent->_bf;}else{++parent->_bf;}if (parent->_bf == 0){break;}else if (parent->_bf == 1 || parent->_bf == -1){pcur = parent;parent = parent->_parent;}else if (parent->_bf == 2 || parent->_bf == -2){//旋转}} return true;
}
3.旋转
新插入节点以及更新平衡因子如上述,那么在更新平衡因子过程中,遇到平衡因子等于2(也就是以parent为根节点的子树不平衡)需要进行旋转,那如何旋转的呢?
旋转的规则:
- 保持搜索树的原则。
- 将要旋转的树从不满足到满足平衡,其次降低树的高度。
旋转一共分为四种(左单旋、右单旋、左右双旋、右左双旋),其每一种旋转都对应一种情况;
左单旋
先来看一下这种情况:

如上图中以
5为根节点的子树,其中a、b、c都是高度为h的子树(h可以等于0);现在要在a子树中插入一个节点,在更新平衡因子的过程中,5所在节点的平衡因子1 -> 2,此时该子树就不平衡了,需要进行旋转;
通过观察上图,我们可以发现,5节点的右子树太高了,所以我们需要向左旋转来平衡高度;
如何旋转呢?
旋转步骤
b子树变成5节点的右子树;- 以
5节点为根节点的子树变成10节点的左子树;10节点就变成了这个子树新的根节点。

其中5<
b子树<10,所以将b子树变成5的右子树,以5为根节点的子树变成10的左子树,仍然满足搜索二叉树的规则;然后
10节点变成了这部分子树新的根节点。(并不一定是整个子树新的根节点)。
代码实现:
//左单旋
void RevoleL(Node* parent)
{//旋转节点的右孩子节点Node* subr = parent->_right;//旋转节点的右孩子节点的左孩子节点Node* subrl = parent->_right->_left;//subrl变成parent的右子树parent->_right = subrl;//subrl可能为空if (subrl)subrl->_parent = parent;//parent->变成subr的左子树subr->_left = parent;//记录parent的父节点Node* pnode = parent->_parent;parent->_parent = subr;//如果parent是整个avl树的根节点if (pnode == nullptr){_root = subr;subr->_parent = nullptr;}else{//parent父节点不为空subr->_parent = pnode;if (pnode->_left == parent){pnode->_left = subr;}else{pnode->_right = subr;}}//调整完之后将parent节点与subr节点的平衡因子修改成0parent->_bf = 0;subr->_bf = 0;
}
右单旋
了解了左单旋,右单旋就十分简单了:

和左单旋的情况相似,有单旋就是10节点的左子树高,需要进行右单旋;
旋转步骤
b子树变成10节点的左子树;- 以
10节点为根节点的子树变成5节点的右子树;5节点就变成这部分子树的根节点。

其中
5<b子树<10,所以将b子树变成10的左子树,以10为根节点的子树变成5的右子树;仍然保持搜索二叉树的结构。
5节点就变成了这部分子树的根节点。
代码实现
//右单旋
void RevoleR(Node* parent)
{//旋转节点的左孩子节点Node* subl = parent->_left;//旋转节点的左孩子节点的右孩子节点Node* sublr = parent->_left->_right;//sublr变成parent的左孩子节点parent->_left = sublr;//sublr可能为nullptrif (sublr)sublr->_parent = parent;//parent变成subl的右孩子节点subl->_right = parent;//记录parent的父节点Node* pnode = parent->_parent;if (pnode == nullptr){_root = subl;subl->_parent = nullptr;}else{subl->_parent = pnode;if (pnode->_left == parent){pnode->_left = subl;}else{pnode->_right = subl;}}//修改parent 和 subl 的平衡因子parent->_bf = 0;subl->_bf = 0;
}
左右双旋
左单旋、右单旋都是纯粹的一边高(就是在parent的左/右孩子的左/右孩子所在子树中插入数据);按上述说,就是在a子树中插入数据,但是如果是在b子树中插入数据呢?

如上图,我们很显然不能单纯的使用右单旋或者左单旋来解决问题了;
旋转步骤
左右双旋其实就是,先对parent的左孩子节点进行一次左单选,再对parent节点进行一次右单旋;
来看分析:
这里h是能够等于0的,我们分开来讨论:
h=0

我们先对subl节点进行一次左单旋,再对parent节点进行一次右单旋;

h!=0
对于h!=0的情况,b子树中就至少有一个节点,那我们要分为两种情况讨论;
我们将一个avl树抽象成下面这种情况:

这样我们可以看出来,可能是在e子树中插入数据,也可能是在f子树中插入数据;那这两种情况就也要分开讨论:
在e子树中插入

此时,我们还是先对subl节点左单旋,变成纯粹的一边高,再对parent节点进行右单旋;

在f子树插入节点

还是先对subl左单旋,再对parent进行右单旋;

通过观察,我们可以发现,这三种情况都是进行了一次左单旋和一次右单旋,不同的是其结果中subl和parent的平衡因子不同。
这样我们在实现时,就直接复用左单旋和右单旋就好了,然后根据其平衡因子的情况来判断最后subl和parent节点的平衡因子即可。
更新平衡因子
sublr节点平衡因子等于0:sublr、subl和parent平衡因子都为0;sublr节点平衡因子等于-1:sublr和subl平衡因子等于0,parent平衡因子等于1;sublr节点平衡因子等于1:sublr和parent平衡因子等于0,subl平衡因子等于-1;
代码实现
代码实现过程中有一个细节就是:
在进行左右单旋时,会将平衡因子修改成
0,我们就需要先记录一下sublr原本的平衡因子,来保证我们单旋结束后的平衡因子的修改。
//左右双选void RevoleLR(Node* parent) {Node* subl = parent->_left;Node* sublr = parent->_left->_right;int bf = sublr->_bf;//对subl进行左单旋RevoleL(subl);//对parent进行右单旋RevoleR(parent);//更新平衡因子if (bf == 0){parent->_bf = 0;subl->_bf = 0;sublr->_bf = 0;}else if (bf == 1){parent->_bf = 0;subl->_bf = -1;sublr->_bf = 0;}else if (bf == -1){parent->_bf = 1;subl->_bf = 0;sublr->_bf = 0;}}
右左双旋
右左双旋和左右双旋逻辑非常像,这里就不演示了,直接看代码实现:
//右左双选void RevoleRL(Node* parent) {Node* subr = parent->_right;Node* subrl = parent->_right->_left;int bf = subrl->_bf;RevoleR(subr);RevoleL(parent);if (bf == 0){parent->_bf = 0;subr->_bf = 0;subrl->_bf = 0;}else if (bf == 1){parent->_bf = -1;subr->_bf = 0;subrl->_bf = 0;}else if (bf == -1){parent->_bf = 0;subr->_bf = 1;subrl->_bf = 0;}}
在旋转实现完成之后我们就可以完善我们insert了:
//插入
bool insert(const pair<K, V> kv)
{Node* newnode = Node(kv);if (_root == nullptr){_root = newnode;return true;}Node* parent = nullptr;Node* pcur = _root;while (pcur){if (kv.first > pcur->_kv.first){parent = pcur;pcur = pcur->_right;}else if (kv.first < pcur->_kv.first){parent = pcur;pcur = pcur->_left;}else{return false;}}pcur = newnode;newnode->_parent = parent;if (kv.first > parent->_kv.first){parent->_right = pcur;}else if (kv.first < parent->_kv.first){parent->_left = pcur;}else{return false;}//更新平衡因子while (parent){if (pcur == parent->_left){--parent->_bf;}else{++parent->_bf;}if (parent->_bf == 0){break;}else if (parent->_bf == 1 || parent->_bf == -1){pcur = parent;parent = parent->_parent;}else if (parent->_bf == 2 || parent->_bf == -2){//旋转if (parent->_bf == 2 && parent->_left->_bf == 1){//左单旋RevoleL(parent);}else if (parent->_bf == 2 && parent->_left->_bf == -1){//右左双旋RevoleRL(parent);}else if (parent->_bf == -2 && parent->_right->_bf == -1){//右单旋RevoleR(parent);}else if (parent->_bf == -2 && parent->_left->_bf == 1){//左右双旋RevoleLR(parent);}}}return true;
}
旋转了解完以后,就可以完善之前的插入功能了:
//插入
bool insert(const pair<K, V> kv)
{Node* newnode = new Node(kv);if (_root == nullptr){_root = newnode;return true;}Node* parent = nullptr;Node* pcur = _root;while (pcur){if (kv.first > pcur->_kv.first){parent = pcur;pcur = pcur->_right;}else if (kv.first < pcur->_kv.first){parent = pcur;pcur = pcur->_left;}else{return false;}}pcur = newnode;newnode->_parent = parent;if (kv.first > parent->_kv.first){parent->_right = pcur;}else if (kv.first < parent->_kv.first){parent->_left = pcur;}else{return false;}//更新平衡因子while (parent){if (pcur == parent->_left){--parent->_bf;}else{++parent->_bf;}if (parent->_bf == 0){break;}else if (parent->_bf == 1 || parent->_bf == -1){pcur = parent;parent = parent->_parent;}else if (parent->_bf == 2 || parent->_bf == -2){//旋转if (parent->_bf == 2 && parent->_left->_bf == 1){//左单旋RevoleL(parent);}else if (parent->_bf == 2 && parent->_left->_bf == -1){//右左双旋RevoleRL(parent);}else if (parent->_bf == -2 && parent->_right->_bf == -1){//右单旋RevoleR(parent);}else if (parent->_bf == -2 && parent->_left->_bf == 1){//左右双旋RevoleLR(parent);}}}return true;
}
4. AVL树的查找
AVL树的查找先对就简单多了,和搜索二叉树查找一样。
//查找bool find(const K& kv) {Node* ptail = _root;while (ptail){if (kv.first > ptail->_kv->first){ptail = ptail->_right;}else if (kv.first < ptail->_kv->first){ptail = ptail->_left;}else{return true;}}return false;}
对于
AVL树的删除,有点过于复杂,感兴趣的可以深入探究一下;后面研究过了再来探讨这个问题。
}else if (parent->_bf == -2 && parent->_right->_bf == -1){//右单旋RevoleR(parent);}else if (parent->_bf == -2 && parent->_left->_bf == 1){//左右双旋RevoleLR(parent);}}
}
return true;
}
## 4. `AVL`树的查找`AVL`树的查找先对就简单多了,和搜索二叉树查找一样。```cpp//查找bool find(const K& kv) {Node* ptail = _root;while (ptail){if (kv.first > ptail->_kv->first){ptail = ptail->_right;}else if (kv.first < ptail->_kv->first){ptail = ptail->_left;}else{return true;}}return false;}
对于
AVL树的删除,有点过于复杂,感兴趣的可以深入探究一下;后面研究过了再来探讨这个问题。
我的博客即将同步至腾讯云开发者社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=2oul0hvapjsws
相关文章:
【AVL树】—— 我与C++的不解之缘(二十三)
什么是AVL树? AVL树发明者是G. M. Adelson-Velsky和E. M. Landis两个前苏联科学家,他们在1962年论文《An algorithm for the organization of information》中发表了AVL树。AVL树是最先发明的自平衡二叉搜索树,说白了就是能够自己控制平衡结构…...
用大白话解释日志处理Log4j 是什么 有什么用 怎么用
Log4j是什么? Log4j就像程序的“黑匣子”,专门用来记录软件运行时的各种信息,比如哪里报错、性能如何、用户操作轨迹等。它是Java领域最常用的日志框架之一,可以灵活控制日志内容、输出位置(控制台、文件、数据库等&a…...
无人机遥控器的亮度 和 两个工作频率
工作频率 2.4000-2.4835 GHz , 5.725-5.850 GHz 1.这是一个无人机的遥控器的两个工作频率,为什么会有两个工作频率? 无人机的遥控器采用双频段设计(2.4GHz 和 5.8GHz),主要是为了解决以下问题并优化性…...
【Linux】命令行参数 | 环境变量(四)
目录 前言: 一、命令行参数: 1.main函数参数 2.为什么有它? 二、环境变量: 1.main函数第三个参数 2.查看shell本身环境变量 3.PATH环境变量 4.修改PATH环境变量配置文件 5.HOME环境变量 6.SHELL环境变量 7.PWD环境变…...
算法002——复写零
力扣——复写零点击即可跳转 这道题还是运用 双指针,我们从左往右开始,让 cur 0,dest 0,当我们循环时,会覆盖后面的值,所以从左到右无法实现,我们运用 从右到左的方式。 以示例一数组为例,从…...
例子 DQN + CartPole: 深入思考一下,强化学习确实是一场智能冒险之旅!
强化学习的概念 在技术人员眼里,深度学习、强化学习,或者是大模型,都只是一些算法。无论是简单,还是复杂,我们都是平静的看待。当商业元素日益渗透进技术领域,人人言必称大模型的时候。技术人该反思一下&a…...
java 实现xxl-job定时任务自动注册到调度中心
xxl-job 自动注册(执行器和任务) 前言 xxl-job是一个功能强大、简单易用、高可用且可扩展性强的分布式定时任务框架/分布式任务调度平台。它适用于各种需要定时任务调度的场景,并可根据业务需求进行灵活配置和扩展。 xxl-job简介 xxl-job是一个开源的分布式定时任务框架,…...
esp32串口通信
1、线路图 2、打开电脑的串口终端 3、eps32通过串口往电脑的串口终端输出信息: from machine import UART, Pin import time# 初始化UART0,波特率设置为115200 uart UART(0, baudrate115200, tx1, rx3)# 主循环 while True:# 要发送的消息#某些串口终…...
蓝桥杯备赛-前缀和-可获得的最小取值
问题描述 妮妮学姐手头有一个长度为 nn 的数组 aa,她想进行 kk 次操作来取出数组中的元素。每次操作必须选择以下两种操作之一: 取出数组中的最大元素。取出数组中的最小元素和次小元素。 妮妮学姐希望在进行完 kk 次操作后,取出的数的和最…...
UniApp 中封装 HTTP 请求与 Token 管理(附Demo)
目录 1. 基本知识2. Demo3. 拓展 1. 基本知识 从实战代码中学习,上述实战代码来源:芋道源码/yudao-mall-uniapp 该代码中,通过自定义 request 函数对 HTTP 请求进行了统一管理,并且结合了 Token 认证机制 请求封装原理ÿ…...
边缘计算+多模态感知:户外监控核心技术解析与工程部署实践!户外摄像头监控哪种好?户外摄像头监控十大品牌!格行视精灵VS海康威视VS大华横评!
一、核心参数解析与选型逻辑 1.环境适应性设计 极端天气防护:优先选择IP66/67防护等级的设备,例如格行视精灵通过IP67防水防尘设计可应对暴雨、沙尘暴等复杂环境,其密封轴承结构可有效防止水汽侵蚀内部电路。 温度耐受范围:北方…...
Spring项目-抽奖系统(实操项目)(ONE)
^__^ (oo)\______ (__)\ )\/\ ||----w | || || 一:前言: 随着互联网技术的快速发展,线上营销活动已成为企业吸引用户、…...
STM32-智能小车项目
项目框图 ST-link接线 实物图: 正面: 反面: 相关内容 使用L9110S电机模块 电机驱动模块L9110S详解 | 良许嵌入式 测速模块 语音模块SU-03T 网站:智能公元/AI产品零代码平台 一、让小车动起来 新建文件夹智能小车项目 在里面…...
Python:字符串常见操作
find(子字符串,开始位置下标,结束位置下标) 注意:开始位置和结束位置下标可以省略,表示在整个字符串中查找 stasdfghjkl print(st.find(a))#输出结果为0,表明a在第一个位置默认从零开始,找不到则返回-1 …...
Redis 哈希(Hash)
Redis 哈希(Hash) 概述 Redis 哈希(Hash)是一种特殊的键值对类型,它允许存储结构化的数据,例如一个对象或记录。每个哈希值可以包含多个字段,每个字段又可以存储一个字符串值。这使得Redis哈希非常适合用于存储对象的…...
Windows对比MacOS
Windows对比MacOS 文章目录 Windows对比MacOS1-环境变量1-Windows添加环境变量示例步骤 1:打开环境变量设置窗口步骤 2:添加系统环境变量 2-Mac 系统添加环境变量示例步骤 1:打开终端步骤 2:编辑环境变量配置文件步骤 3࿱…...
react 路由跳转的几种方式
在 React 中,路由跳转通常通过 react-router-dom(或类似的路由库)来实现。以下是几种常见的路由跳转方式: 1. 使用 <Link> 组件 <Link> 是最简单的路由跳转方式,它会生成一个 <a> 标签,…...
2.你有什么绝活儿?—Java能做什么?
1、Java的绝活儿:要问Java有什么绝活,我觉得它应该算是一位魔法师,会的绝活儿有很多,要说最能拿得出手的当属以下三个。 1.1 平台无关性:Java可以在任何地方施展魔法,无论是Windows、Linux还是Mac…...
2025年2月文章一览
2025年2月编程人总共更新了17篇文章: 1.2025年1月文章一览 2.《Operating System Concepts》阅读笔记:p2-p8 3.《Operating System Concepts》阅读笔记:p9-p12 4.《Operating System Concepts》阅读笔记:p13-p16 5.《Operati…...
C++ | 面向对象 | 类
👻类 👾语法格式 class className{Access specifiers: // 访问权限DataType variable; // 变量returnType functions() { } // 方法 };👾访问权限 class className {public:// 公有成员protected:// 受保护成员private:// 私有成员 }…...
Vue记事本应用实现教程
文章目录 1. 项目介绍2. 开发环境准备3. 设计应用界面4. 创建Vue实例和数据模型5. 实现记事本功能5.1 添加新记事项5.2 删除记事项5.3 清空所有记事 6. 添加样式7. 功能扩展:显示创建时间8. 功能扩展:记事项搜索9. 完整代码10. Vue知识点解析10.1 数据绑…...
Prompt Tuning、P-Tuning、Prefix Tuning的区别
一、Prompt Tuning、P-Tuning、Prefix Tuning的区别 1. Prompt Tuning(提示调优) 核心思想:固定预训练模型参数,仅学习额外的连续提示向量(通常是嵌入层的一部分)。实现方式:在输入文本前添加可训练的连续向量(软提示),模型只更新这些提示参数。优势:参数量少(仅提…...
SciencePlots——绘制论文中的图片
文章目录 安装一、风格二、1 资源 安装 # 安装最新版 pip install githttps://github.com/garrettj403/SciencePlots.git# 安装稳定版 pip install SciencePlots一、风格 简单好用的深度学习论文绘图专用工具包–Science Plot 二、 1 资源 论文绘图神器来了:一行…...
云启出海,智联未来|阿里云网络「企业出海」系列客户沙龙上海站圆满落地
借阿里云中企出海大会的东风,以**「云启出海,智联未来|打造安全可靠的出海云网络引擎」为主题的阿里云企业出海客户沙龙云网络&安全专场于5.28日下午在上海顺利举办,现场吸引了来自携程、小红书、米哈游、哔哩哔哩、波克城市、…...
江苏艾立泰跨国资源接力:废料变黄金的绿色供应链革命
在华东塑料包装行业面临限塑令深度调整的背景下,江苏艾立泰以一场跨国资源接力的创新实践,重新定义了绿色供应链的边界。 跨国回收网络:废料变黄金的全球棋局 艾立泰在欧洲、东南亚建立再生塑料回收点,将海外废弃包装箱通过标准…...
linux 错误码总结
1,错误码的概念与作用 在Linux系统中,错误码是系统调用或库函数在执行失败时返回的特定数值,用于指示具体的错误类型。这些错误码通过全局变量errno来存储和传递,errno由操作系统维护,保存最近一次发生的错误信息。值得注意的是,errno的值在每次系统调用或函数调用失败时…...
使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装
以下是基于 vant-ui(适配 Vue2 版本 )实现截图中照片上传预览、删除功能,并封装成可复用组件的完整代码,包含样式和逻辑实现,可直接在 Vue2 项目中使用: 1. 封装的图片上传组件 ImageUploader.vue <te…...
【决胜公务员考试】求职OMG——见面课测验1
2025最新版!!!6.8截至答题,大家注意呀! 博主码字不易点个关注吧,祝期末顺利~~ 1.单选题(2分) 下列说法错误的是:( B ) A.选调生属于公务员系统 B.公务员属于事业编 C.选调生有基层锻炼的要求 D…...
【python异步多线程】异步多线程爬虫代码示例
claude生成的python多线程、异步代码示例,模拟20个网页的爬取,每个网页假设要0.5-2秒完成。 代码 Python多线程爬虫教程 核心概念 多线程:允许程序同时执行多个任务,提高IO密集型任务(如网络请求)的效率…...
汇编常见指令
汇编常见指令 一、数据传送指令 指令功能示例说明MOV数据传送MOV EAX, 10将立即数 10 送入 EAXMOV [EBX], EAX将 EAX 值存入 EBX 指向的内存LEA加载有效地址LEA EAX, [EBX4]将 EBX4 的地址存入 EAX(不访问内存)XCHG交换数据XCHG EAX, EBX交换 EAX 和 EB…...
