【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:// 私有成员 }…...
【Java学习笔记】Arrays类
Arrays 类 1. 导入包:import java.util.Arrays 2. 常用方法一览表 方法描述Arrays.toString()返回数组的字符串形式Arrays.sort()排序(自然排序和定制排序)Arrays.binarySearch()通过二分搜索法进行查找(前提:数组是…...

MFC内存泄露
1、泄露代码示例 void X::SetApplicationBtn() {CMFCRibbonApplicationButton* pBtn GetApplicationButton();// 获取 Ribbon Bar 指针// 创建自定义按钮CCustomRibbonAppButton* pCustomButton new CCustomRibbonAppButton();pCustomButton->SetImage(IDB_BITMAP_Jdp26)…...
Neo4j 集群管理:原理、技术与最佳实践深度解析
Neo4j 的集群技术是其企业级高可用性、可扩展性和容错能力的核心。通过深入分析官方文档,本文将系统阐述其集群管理的核心原理、关键技术、实用技巧和行业最佳实践。 Neo4j 的 Causal Clustering 架构提供了一个强大而灵活的基石,用于构建高可用、可扩展且一致的图数据库服务…...
【HTTP三个基础问题】
面试官您好!HTTP是超文本传输协议,是互联网上客户端和服务器之间传输超文本数据(比如文字、图片、音频、视频等)的核心协议,当前互联网应用最广泛的版本是HTTP1.1,它基于经典的C/S模型,也就是客…...

QT: `long long` 类型转换为 `QString` 2025.6.5
在 Qt 中,将 long long 类型转换为 QString 可以通过以下两种常用方法实现: 方法 1:使用 QString::number() 直接调用 QString 的静态方法 number(),将数值转换为字符串: long long value 1234567890123456789LL; …...

分布式增量爬虫实现方案
之前我们在讨论的是分布式爬虫如何实现增量爬取。增量爬虫的目标是只爬取新产生或发生变化的页面,避免重复抓取,以节省资源和时间。 在分布式环境下,增量爬虫的实现需要考虑多个爬虫节点之间的协调和去重。 另一种思路:将增量判…...
智能职业发展系统:AI驱动的职业规划平台技术解析
智能职业发展系统:AI驱动的职业规划平台技术解析 引言:数字时代的职业革命 在当今瞬息万变的就业市场中,传统的职业规划方法已无法满足个人和企业的需求。据统计,全球每年有超过2亿人面临职业转型困境,而企业也因此遭…...
机器学习的数学基础:线性模型
线性模型 线性模型的基本形式为: f ( x ) ω T x b f\left(\boldsymbol{x}\right)\boldsymbol{\omega}^\text{T}\boldsymbol{x}b f(x)ωTxb 回归问题 利用最小二乘法,得到 ω \boldsymbol{\omega} ω和 b b b的参数估计$ \boldsymbol{\hat{\omega}}…...

CTF show 数学不及格
拿到题目先查一下壳,看一下信息 发现是一个ELF文件,64位的 用IDA Pro 64 打开这个文件 然后点击F5进行伪代码转换 可以看到有五个if判断,第一个argc ! 5这个判断并没有起太大作用,主要是下面四个if判断 根据题目…...
AWS vs 阿里云:功能、服务与性能对比指南
在云计算领域,Amazon Web Services (AWS) 和阿里云 (Alibaba Cloud) 是全球领先的提供商,各自在功能范围、服务生态系统、性能表现和适用场景上具有独特优势。基于提供的引用[1]-[5],我将从功能、服务和性能三个方面进行结构化对比分析&#…...