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

【AVL树】—— 我与C++的不解之缘(二十三)

什么是AVL树?

  • AVL树发明者是G. M. Adelson-VelskyE. 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为根节点的子树,其中abc都是高度为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进行右单旋;

在这里插入图片描述

通过观察,我们可以发现,这三种情况都是进行了一次左单旋和一次右单旋,不同的是其结果中sublparent的平衡因子不同。

这样我们在实现时,就直接复用左单旋右单旋就好了,然后根据其平衡因子的情况来判断最后sublparent节点的平衡因子即可。

更新平衡因子

  • sublr节点平衡因子等于0sublrsublparent平衡因子都为0
  • sublr节点平衡因子等于-1sublrsubl平衡因子等于0parent平衡因子等于1
  • sublr节点平衡因子等于1sublrparent平衡因子等于0subl平衡因子等于-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树&#xff1f; AVL树发明者是G. M. Adelson-Velsky和E. M. Landis两个前苏联科学家&#xff0c;他们在1962年论文《An algorithm for the organization of information》中发表了AVL树。AVL树是最先发明的自平衡二叉搜索树&#xff0c;说白了就是能够自己控制平衡结构…...

用大白话解释日志处理Log4j 是什么 有什么用 怎么用

Log4j是什么&#xff1f; Log4j就像程序的“黑匣子”&#xff0c;专门用来记录软件运行时的各种信息&#xff0c;比如哪里报错、性能如何、用户操作轨迹等。它是Java领域最常用的日志框架之一&#xff0c;可以灵活控制日志内容、输出位置&#xff08;控制台、文件、数据库等&a…...

无人机遥控器的亮度 和 两个工作频率

工作频率 2.4000-2.4835 GHz &#xff0c; 5.725-5.850 GHz 1.这是一个无人机的遥控器的两个工作频率&#xff0c;为什么会有两个工作频率&#xff1f; 无人机的遥控器采用双频段设计&#xff08;2.4GHz 和 5.8GHz&#xff09;&#xff0c;主要是为了解决以下问题并优化性…...

【Linux】命令行参数 | 环境变量(四)

目录 前言&#xff1a; 一、命令行参数&#xff1a; 1.main函数参数 2.为什么有它&#xff1f; 二、环境变量&#xff1a; 1.main函数第三个参数 2.查看shell本身环境变量 3.PATH环境变量 4.修改PATH环境变量配置文件 5.HOME环境变量 6.SHELL环境变量 7.PWD环境变…...

算法002——复写零

力扣——复写零点击即可跳转 这道题还是运用 双指针&#xff0c;我们从左往右开始&#xff0c;让 cur 0&#xff0c;dest 0,当我们循环时&#xff0c;会覆盖后面的值&#xff0c;所以从左到右无法实现&#xff0c;我们运用 从右到左的方式。 以示例一数组为例&#xff0c;从…...

例子 DQN + CartPole: 深入思考一下,强化学习确实是一场智能冒险之旅!

强化学习的概念 在技术人员眼里&#xff0c;深度学习、强化学习&#xff0c;或者是大模型&#xff0c;都只是一些算法。无论是简单&#xff0c;还是复杂&#xff0c;我们都是平静的看待。当商业元素日益渗透进技术领域&#xff0c;人人言必称大模型的时候。技术人该反思一下&a…...

java 实现xxl-job定时任务自动注册到调度中心

xxl-job 自动注册(执行器和任务) 前言 xxl-job是一个功能强大、简单易用、高可用且可扩展性强的分布式定时任务框架/分布式任务调度平台。它适用于各种需要定时任务调度的场景,并可根据业务需求进行灵活配置和扩展。 xxl-job简介 xxl-job是一个开源的分布式定时任务框架,…...

esp32串口通信

1、线路图 2、打开电脑的串口终端 3、eps32通过串口往电脑的串口终端输出信息&#xff1a; from machine import UART, Pin import time# 初始化UART0&#xff0c;波特率设置为115200 uart UART(0, baudrate115200, tx1, rx3)# 主循环 while True:# 要发送的消息#某些串口终…...

蓝桥杯备赛-前缀和-可获得的最小取值

问题描述 妮妮学姐手头有一个长度为 nn 的数组 aa&#xff0c;她想进行 kk 次操作来取出数组中的元素。每次操作必须选择以下两种操作之一&#xff1a; 取出数组中的最大元素。取出数组中的最小元素和次小元素。 妮妮学姐希望在进行完 kk 次操作后&#xff0c;取出的数的和最…...

UniApp 中封装 HTTP 请求与 Token 管理(附Demo)

目录 1. 基本知识2. Demo3. 拓展 1. 基本知识 从实战代码中学习&#xff0c;上述实战代码来源&#xff1a;芋道源码/yudao-mall-uniapp 该代码中&#xff0c;通过自定义 request 函数对 HTTP 请求进行了统一管理&#xff0c;并且结合了 Token 认证机制 请求封装原理&#xff…...

边缘计算+多模态感知:户外监控核心技术解析与工程部署实践!户外摄像头监控哪种好?户外摄像头监控十大品牌!格行视精灵VS海康威视VS大华横评!

一、核心参数解析与选型逻辑 1.环境适应性设计 极端天气防护&#xff1a;优先选择IP66/67防护等级的设备&#xff0c;例如格行视精灵通过IP67防水防尘设计可应对暴雨、沙尘暴等复杂环境&#xff0c;其密封轴承结构可有效防止水汽侵蚀内部电路。 温度耐受范围&#xff1a;北方…...

Spring项目-抽奖系统(实操项目)(ONE)

^__^ (oo)\______ (__)\ )\/\ ||----w | || || 一&#xff1a;前言&#xff1a; 随着互联网技术的快速发展&#xff0c;线上营销活动已成为企业吸引用户、…...

STM32-智能小车项目

项目框图 ST-link接线 实物图&#xff1a; 正面&#xff1a; 反面&#xff1a; 相关内容 使用L9110S电机模块 电机驱动模块L9110S详解 | 良许嵌入式 测速模块 语音模块SU-03T 网站&#xff1a;智能公元/AI产品零代码平台 一、让小车动起来 新建文件夹智能小车项目 在里面…...

Python:字符串常见操作

find(子字符串&#xff0c;开始位置下标&#xff0c;结束位置下标) 注意&#xff1a;开始位置和结束位置下标可以省略&#xff0c;表示在整个字符串中查找 stasdfghjkl print(st.find(a))#输出结果为0&#xff0c;表明a在第一个位置默认从零开始&#xff0c;找不到则返回-1 …...

Redis 哈希(Hash)

Redis 哈希(Hash) 概述 Redis 哈希&#xff08;Hash&#xff09;是一种特殊的键值对类型&#xff0c;它允许存储结构化的数据&#xff0c;例如一个对象或记录。每个哈希值可以包含多个字段&#xff0c;每个字段又可以存储一个字符串值。这使得Redis哈希非常适合用于存储对象的…...

Windows对比MacOS

Windows对比MacOS 文章目录 Windows对比MacOS1-环境变量1-Windows添加环境变量示例步骤 1&#xff1a;打开环境变量设置窗口步骤 2&#xff1a;添加系统环境变量 2-Mac 系统添加环境变量示例步骤 1&#xff1a;打开终端步骤 2&#xff1a;编辑环境变量配置文件步骤 3&#xff1…...

react 路由跳转的几种方式

在 React 中&#xff0c;路由跳转通常通过 react-router-dom&#xff08;或类似的路由库&#xff09;来实现。以下是几种常见的路由跳转方式&#xff1a; 1. 使用 <Link> 组件 <Link> 是最简单的路由跳转方式&#xff0c;它会生成一个 <a> 标签&#xff0c…...

2.你有什么绝活儿?—Java能做什么?

1、Java的绝活儿&#xff1a;要问Java有什么绝活&#xff0c;我觉得它应该算是一位魔法师&#xff0c;会的绝活儿有很多&#xff0c;要说最能拿得出手的当属以下三个。 1.1 平台无关性&#xff1a;Java可以在任何地方施展魔法&#xff0c;无论是Windows、Linux还是Mac&#xf…...

2025年2月文章一览

2025年2月编程人总共更新了17篇文章&#xff1a; 1.2025年1月文章一览 2.《Operating System Concepts》阅读笔记&#xff1a;p2-p8 3.《Operating System Concepts》阅读笔记&#xff1a;p9-p12 4.《Operating System Concepts》阅读笔记&#xff1a;p13-p16 5.《Operati…...

C++ | 面向对象 | 类

&#x1f47b;类 &#x1f47e;语法格式 class className{Access specifiers: // 访问权限DataType variable; // 变量returnType functions() { } // 方法 };&#x1f47e;访问权限 class className {public:// 公有成员protected:// 受保护成员private:// 私有成员 }…...

React 第五十五节 Router 中 useAsyncError的使用详解

前言 useAsyncError 是 React Router v6.4 引入的一个钩子&#xff0c;用于处理异步操作&#xff08;如数据加载&#xff09;中的错误。下面我将详细解释其用途并提供代码示例。 一、useAsyncError 用途 处理异步错误&#xff1a;捕获在 loader 或 action 中发生的异步错误替…...

React Native 导航系统实战(React Navigation)

导航系统实战&#xff08;React Navigation&#xff09; React Navigation 是 React Native 应用中最常用的导航库之一&#xff0c;它提供了多种导航模式&#xff0c;如堆栈导航&#xff08;Stack Navigator&#xff09;、标签导航&#xff08;Tab Navigator&#xff09;和抽屉…...

ssc377d修改flash分区大小

1、flash的分区默认分配16M、 / # df -h Filesystem Size Used Available Use% Mounted on /dev/root 1.9M 1.9M 0 100% / /dev/mtdblock4 3.0M...

高频面试之3Zookeeper

高频面试之3Zookeeper 文章目录 高频面试之3Zookeeper3.1 常用命令3.2 选举机制3.3 Zookeeper符合法则中哪两个&#xff1f;3.4 Zookeeper脑裂3.5 Zookeeper用来干嘛了 3.1 常用命令 ls、get、create、delete、deleteall3.2 选举机制 半数机制&#xff08;过半机制&#xff0…...

智能在线客服平台:数字化时代企业连接用户的 AI 中枢

随着互联网技术的飞速发展&#xff0c;消费者期望能够随时随地与企业进行交流。在线客服平台作为连接企业与客户的重要桥梁&#xff0c;不仅优化了客户体验&#xff0c;还提升了企业的服务效率和市场竞争力。本文将探讨在线客服平台的重要性、技术进展、实际应用&#xff0c;并…...

浪潮交换机配置track检测实现高速公路收费网络主备切换NQA

浪潮交换机track配置 项目背景高速网络拓扑网络情况分析通信线路收费网络路由 收费汇聚交换机相应配置收费汇聚track配置 项目背景 在实施省内一条高速公路时遇到的需求&#xff0c;本次涉及的主要是收费汇聚交换机的配置&#xff0c;浪潮网络设备在高速项目很少&#xff0c;通…...

return this;返回的是谁

一个审批系统的示例来演示责任链模式的实现。假设公司需要处理不同金额的采购申请&#xff0c;不同级别的经理有不同的审批权限&#xff1a; // 抽象处理者&#xff1a;审批者 abstract class Approver {protected Approver successor; // 下一个处理者// 设置下一个处理者pub…...

GruntJS-前端自动化任务运行器从入门到实战

Grunt 完全指南&#xff1a;从入门到实战 一、Grunt 是什么&#xff1f; Grunt是一个基于 Node.js 的前端自动化任务运行器&#xff0c;主要用于自动化执行项目开发中重复性高的任务&#xff0c;例如文件压缩、代码编译、语法检查、单元测试、文件合并等。通过配置简洁的任务…...

深度学习水论文:mamba+图像增强

&#x1f9c0;当前视觉领域对高效长序列建模需求激增&#xff0c;对Mamba图像增强这方向的研究自然也逐渐火热。原因在于其高效长程建模&#xff0c;以及动态计算优势&#xff0c;在图像质量提升和细节恢复方面有难以替代的作用。 &#x1f9c0;因此短时间内&#xff0c;就有不…...

(一)单例模式

一、前言 单例模式属于六大创建型模式,即在软件设计过程中,主要关注创建对象的结果,并不关心创建对象的过程及细节。创建型设计模式将类对象的实例化过程进行抽象化接口设计,从而隐藏了类对象的实例是如何被创建的,封装了软件系统使用的具体对象类型。 六大创建型模式包括…...