红黑树是如何实现的?

文章目录

  • 一、红黑树的概念
  • 二、红黑树的性质
  • 三、红黑树和AVL树对比
  • 四、红黑树的插入
    • 1. 红黑树的结点定义
    • 2. 父亲的颜色
    • 3. 叔叔的颜色为红色
    • 4. 叔叔不存在
    • 5. 叔叔存在且为黑
    • 6. 插入的抽象图
  • 五、红黑树的验证
    • 1. 检查平衡
    • 2. 计算高度与旋转次数
    • 3. 验证
  • 六、 红黑树与AVL树的比较

一、红黑树的概念

红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍(即最长路径不超过最短路径的两倍,近似平衡),因而是接近平衡的。

image-20230921131357000

二、红黑树的性质

  1. 每个结点不是红色就是黑色
  2. 根节点是黑色的
  3. 如果一个节点是红色的,则它的两个孩子结点必须是黑色的(即任何路径没有连续的红色结点)
  4. 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均包含相同数目的黑色结点(每条路径上的黑色结点个数相同,这里的路径包括空结点)
  5. 每个叶子结点都是黑色的(此处的叶子结点指的是空结点,已经不在是传统的叶子结点了,图中的NIL结点就是空结点)

这里对第四点做一补充说明

对于下面这棵树有11条路径,而不是5条路径,因为空结点也包括在内

image-20230921132948276

如果不清楚上面的这一点,如果遇到下面这棵树,可能会误以为他是红黑树,实际上它不是红黑树。

如果我们不看每个空结点的话,它看上去有四条路径,且每条路径都只有两个黑色结点,看上去好像是红黑树。但是实际上要包括空结点,且空结点是黑色的。所以一共有九条路径,最左边的一条路径只有两个黑色结点,其他路径都有三个黑色结点。

image-20230921133042188

第四点中说的虽然是每个结点的每条路径上的黑色结点个数相同,但是由于每个结点的祖先的路径是唯一确定的,所以我们只需要判断从根节点到每个空结点上的路径中黑色结点个数是否相同即可

那么为什么上面的性质可以保证最长路径不超过最长路径的两倍呢?

如下图所示, 最长路径就是黑红相间的情况,最短路径就是全黑的情况。其他路径都是在这两者之间的,此时我们也不难看出最长路径不超过最短路径的两倍。如果最短路径为N,那么最长路径就是2N-1,因为根节点一定是黑色的,最终的叶子结点也一定是黑色的。

image-20230921140144567

三、红黑树和AVL树对比

既然有了AVL树,那么为什么要有红黑树呢?其实是因为AVL树太严格了。它要控制平衡就需要付出代价。而红黑树要求并不严格,综合来看,红黑树的综合性能其实更优

AVL树红黑树
高度对比高度差不超过一最长路径不超过最短路径的两倍
插入1000个值logN---->102*logN---->20
插入10亿个值logN---->302*logN---->60

我们可以看到,虽然他们的高度有点差异,但是他们的效率都是同一个量级的,而且cpu的速度是非常快的,这点效率对于cpu几乎没有任何区别

性能是同一量级的,但是AVL树控制严格平衡是需要付出代价的,插入和删除需要大量的旋转。

四、红黑树的插入

1. 红黑树的结点定义

如下所示, 由于红黑树有红色结点和黑色结点两种颜色。所以不妨我们使用一个枚举类型来进行表示。红黑树里面我们需要有一个pair类型来进行存储key和value类型的数据。然后我们定义三个指针,分别指向父亲,左孩子,右孩子。最后定义其的颜色。在构造函数中,我们将其的颜色默认设置为红色。

设置为红色是比较有讲究的。那是因为我们大多数场景下都是去创建一个新节点去插入的。如果我们插入的这个新结点是黑色的话,那么造成的后果就是违反了规则4,即每条路径上的黑色结点个数相同,显然这种情况要进行补救的话是十分麻烦的。还不如去插入红色结点,如果插入的是红色结点的话,仅仅有可能会违反规则3,也就是红色结点的孩子必须是黑色结点。这一点的话,我们还有的补救,因为仅仅影响本条路径。

enum Color
{RED,BLACK
};template<class K , class V>
struct RBTreeNode
{pair<K, V> _kv;RBTreeNode<K, V>* _left;RBTreeNode<K, V>* _right;RBTreeNode<K, V>* _parent;Color _color;RBTreeNode(const pair<K, V>& kv):_kv(kv),_left(nullptr),_right(nullptr),_parent(nullptr),_color(RED) // 插入红色结点,违反规则3,只影响一条路径,甚至可能不影响。如果插入黑色结点,违反规则4,影响所有路径{}
};

2. 父亲的颜色

因为我们要插入的结点是红色的,那么在我们插入的位置,它的父亲要么是红色的要么是黑色的。如果是黑色的,那么就是如下的情况

image-20230924165738466

我们可以看到,似乎并未影响整棵树的结构,不违反任何一条规则。最短路径仍然不超过最长路径的两倍。每条路径上的黑色结点个数仍然相等。所以如果插入一个新节点以后,如果此处它的父亲是黑色的,完美,不需要做出任何修改即可。如果父亲甚至都不存在,那么这个结点就是这颗树了。我们只需要将这个结点变为黑色即可。如果父亲是红色的话,那么就比较麻烦了。

如下图所示,就是插入的时候父亲为红色,显然已经违反了规则3了,此时我们需要对这颗树进行处理了。

image-20230924174004600

3. 叔叔的颜色为红色

如果我们插入了一个结点以后,此时,父亲结点为红色,且有父亲的兄弟,即叔叔,且叔叔的颜色是红色。

即如下的情况,uncle存在且为红

这样的话,我们可以将parent和uncle都变黑,然后让grandfather变红,即如下的情形

image-20230924181504937

这样的话,在黑色结点数量不变的条件下,使得连续红色结点的问题暂时解决了。现在原本cur为红色的问题转换为了grandfather为红色的问题。

此时的话,如果这个grandfather如果没有父亲了,那么根据规则1,我们只需要让他变为黑色即可。此时仅仅只是所有的路径多了一个黑色结点。如果grandfather有父亲,那么我们只需要让grandfather变为cur,继续向上处理即可

在这里继续向上处理的时候又分为以下几种情况

  1. grandfather没有父亲,那么直接让grandfather变黑即可
  2. grandfather有父亲,且父亲为黑色,那么由于前面的树本身就是满足红黑树规则,这里改变了之后仍然满足红黑树规则,那么不处理即可
  3. grandfather有父亲,且父亲为红色,这样的情况下,父亲为红色,就隐含了必然存在grandfather的grandfather,因为原来的树也要满足红黑树的规则,这样一来就是类似的问题继续往处理即可。

image-20230925163352597

然后由于此时恰好uncle存在且为红,那么我们只需要按照前面的逻辑进行处理即可,即uncle和parent均变黑,然后grandfather变为红

image-20230925163643420

然后又为了满足根节点为红,所以grandfather变为黑即可

image-20230925163816261

4. 叔叔不存在

如下图所示,当叔叔不存在的时候,我们还插入了一个新节点以后,我们发现最长路径已经超过了最短路径的两倍了。这时候单纯的进行变色,已经无法解决问题了。我们需要旋转了。

image-20230925164520373

所以我们就需要进行旋转+变色了。

  • 对于变色:与前面的情况类似,即本来parent和uncle都为红色的话,就把他们两个变为黑色,然后把grandfather变为红色就可以了。不过现在uncle不存在了。那我们就先不管它了,将parent变为黑色,grandfather变为红色就可以了。
  • 对于旋转:这个就需要我们进行具体的分析了,看看究竟是左旋还是右旋还是双旋。具体判断规则与AVL基本是一致的,如果是直线那么就是单旋,我们只需要分析谁高就可以了。如果是折线就是双旋,我们还是分析哪边高就可以了。

如上图所示的情形就是右单旋就可以了

image-20230925170559866

5. 叔叔存在且为黑

我们接着前面的图,我们继续插入一个新的结点

image-20230925171058231

那么此时的uncle存在且为红,我们进行相应的处理后,变为如下的情况

image-20230925171747589

这时候,我们遇到了新的情况,uncle存在且为黑

那么此时的处理情形就和前面的uncle不存在是一样的,直接旋转加变色。这里的如何旋转和变色统一结论后面详细讨论

image-20230925171823196

总结

  1. 红黑树的插入主要看uncle

  2. uncle存在且为红,变色+继续往上更新

  3. uncle不存在或uncle存在且为黑,旋转+变色

6. 插入的抽象图

  • 如下是第一种情况,当cur为红,p为红,g为黑,u存在且为红的条件下。

image-20230926163310656

在这种情况下,我们可以p和u改为黑色,g改为红,然后把g当成cur,继续向上调整。

image-20230926163631710

上面是我们的抽象图,我们如果要具体细化每一种情况的下是非常之麻烦的

比如说当a、b、c、d、e均为空,即cur是新增结点的时候,如下所示

image-20230926172512048

当a,b不为空,且cur是黑色结点。那么cde都是含有一个结点的子树,那么cde的每个样子都有四种情况,如下所示

image-20230926172700176

由于它可以在a,b这两个红色结点任意处进行插入,也就是说,可以在四个位置插入。

image-20230926172742003

那么这种变换的情况就复杂很多,有4*4*4*4共256种情况

如果cde有两个黑节点的话,那么情况将更加复杂,画图已经很难表示出来了

image-20230926174004784

显然我们如果用具象图的话,那么红黑树有无数种情况,所以我们只能使用抽象图来表示这种情况。

所以根据前面的分析,我们就可以写出如下的代码了。下面只是处理颜色的部分。

		//开始处理颜色while (parent && parent->_color == RED){Node* grandfather = parent->_parent;if (grandfather->_left == parent){Node* uncle = grandfather->_right;if (uncle && uncle->_color == RED){parent->_color = BLACK;uncle->_color = BLACK;grandfather->_color = RED;cur = grandfather;parent = grandfather->_parent;}else if (uncle == nullptr || uncle->_color == BLACK){}}else{Node* uncle = grandfather->_left;if (uncle&& uncle->_color = RED){parent->_color = BLACK;uncle->_color = BLACK;grandfather->_color = RED;cur = grandfather;parent = grandfather->_parent;}else if (uncle == nullptr || uncle->_color == BLACK){}}}
  • 接下来,我们讨论第二种情况,即cur为红,p为红,g为黑,u不存在/u存在且为黑

image-20230929185023277

首先来分析uncle不存在的情况下,即如下的情况。此时我们可以注意到,由于uncle不存在,那么cur必然就是新插入的结点。此时我们就根据当前的cur与p的关系来确定是单旋还是双旋。旋转之后,进行变色。在这里的变色中,如果是单旋,就是g和p变色即可。如果是双旋,那么就是cur和g变色

image-20230929171305920

  1. p为g的左孩子,cur为p的左孩子,则进行右单旋转;相反p为g的右孩子,cur为p的右孩子,则进行左单旋转

    p、g变色—p变黑,g变红

  2. p为g的左孩子,cur为p的右孩子,则针对p做左单旋转;相反,p为g的右孩子,cur为p的左孩子,则针对p做右单旋转,此时转化了为了第一步的情况

再来看一下uncle存在且为黑的情况下。由于uncle存在且为黑,所以cur之前必然为黑色的,因为为了满足每条路径上的黑色结点个数相同,就代表着,cur必须为先前向上处理后的,在向上处理过程中,cur变为了红色。

image-20230929185000121

当uncle存在且为黑的情况下,他的变色以及旋转规则都是与uncle不存在是一模一样的

  1. p为g的左孩子,cur为p的左孩子,则进行右单旋转;相反p为g的右孩子,cur为p的右孩子,则进行左单旋转

    p、g变色—p变黑,g变红

  2. p为g的左孩子,cur为p的右孩子,则针对p做左单旋转;相反,p为g的右孩子,cur为p的左孩子,则针对p做右单旋转,此时转化了为了第一步的情况

所以最终插入的完整代码为

	bool Insert(const pair<K, V>& kv){if (_root == nullptr){_root = new Node(kv);_root->_color = BLACK; //根节点必须是黑色return true;}Node* cur = _root;Node* parent = nullptr;while (cur){if (cur->_kv.first > kv.first){parent = cur;cur = cur->_left;}else if (cur->_kv.first < kv.first){parent = cur;cur = cur->_right;}else{return false;}}cur = new Node(kv); //插入红色结点if (parent->_kv.first < kv.first){parent->_right = cur;}else if (parent->_kv.first > kv.first){parent->_left = cur;}cur->_parent = parent;//开始处理颜色while (parent && parent->_color == RED){Node* grandfather = parent->_parent;if (grandfather->_left == parent){Node* uncle = grandfather->_right;if (uncle && uncle->_color == RED){parent->_color = BLACK;uncle->_color = BLACK;grandfather->_color = RED;cur = grandfather;parent = grandfather->_parent;}else if (uncle == nullptr || uncle->_color == BLACK){if (parent->_left == cur){RotateR(grandfather);parent->_color = BLACK;grandfather->_color = RED;break;}else{RotateL(parent);RotateR(grandfather);cur->_color = BLACK;grandfather->_color = RED;break;}}}else{Node* uncle = grandfather->_left;if (uncle && uncle->_color == RED){parent->_color = BLACK;uncle->_color = BLACK;grandfather->_color = RED;cur = grandfather;parent = grandfather->_parent;}else if (uncle == nullptr || uncle->_color == BLACK){if (parent->_right == cur){RotateL(grandfather);grandfather->_color = RED;parent->_color = BLACK;break;}else{RotateR(parent);RotateL(grandfather);cur->_color = BLACK;grandfather->_color = RED;break;}}}}_root->_color = BLACK;return true;}void RotateL(Node* parent){Node* cur = parent->_right;Node* curleft = cur->_left;Node* ppnode = parent->_parent;//改变parentparent->_right = curleft;parent->_parent = cur;//改变curleftif (curleft != nullptr){curleft->_parent = parent;}//改变curcur->_left = parent;cur->_parent = ppnode;//改变ppnodeif (ppnode == nullptr){_root = cur;}else{if (ppnode->_left == parent){ppnode->_left = cur;}else{ppnode->_right = cur;}}}void RotateR(Node* parent){Node* cur = parent->_left;Node* curright = cur->_right;Node* ppnode = parent->_parent;//改变parentparent->_left = curright;parent->_parent = cur;//改变currightif (curright != nullptr){curright->_parent = parent;}//改变curcur->_right = parent;cur->_parent = ppnode;//改变ppnodeif (ppnode == nullptr){_root = cur;}else{if (ppnode->_left == parent){ppnode->_left = cur;}else{ppnode->_right = cur;}}}

五、红黑树的验证

当我们写完一个比较复杂的程序的时候,我们最好去写一个辅助代码去验证它

1. 检查平衡

如下代码所示,可以去检测我们实现的红黑树当插入数据以后是否会出现不平衡的现象。检查利用的就是每条路径上黑色结点个数相同与不能出现连续的两个红色结点这两条规则。

	bool IsBalance(){return IsBalance(_root);}bool IsBalance(Node* root){if (root == nullptr){return true;}if (root->_color == RED){return false;}int benchmark = 0;Node* cur = root;while (cur){if (cur->_color == BLACK){benchmark++;}cur = cur->_left;}return CheckColor(root, 0, benchmark);}bool CheckColor(Node* root, int blacknum, int benchmark){//检查每条路径的黑色结点数量是否相等if (root == nullptr){if (blacknum != benchmark){return false;}return true;}if (root->_color == BLACK){blacknum++;}//检查颜色if (root->_color == RED && root->_parent && root->_parent->_color == RED){cout << root->_kv.first << ":" << "出现两个连续的红色结点" << endl;return false;}return CheckColor(root->_left, blacknum, benchmark) && CheckColor(root->_right, blacknum, benchmark);}

2. 计算高度与旋转次数

高度的代码很简单,如下所示

	int Height(){return Height(_root);}int Height(Node* root){if (root == nullptr){return 0;}int leftheight = Height(root->_left);int rightheight = Height(root->_right);return leftheight > rightheight ? 1 + leftheight : 1 + rightheight;}

对于旋转次数,我们可以直接设置一个变量专门计数。每旋转一次这个值加一次

	int Getrotatecount(){return _rotatecount;}

3. 验证

int main()
{RBTree<int, int> rb;srand(time(0));for (int i = 0; i < 10000; i++){int tmp = rand();rb.Insert(make_pair(tmp, tmp));//rb.Insert(make_pair(i, i));//cout << rb.IsBalance() << endl;//cout << i << ":" << tmp << endl;}cout << "height:" << rb.Height() << endl;cout << "rotatecount:" << rb.Getrotatecount() << endl;cout << rb.IsBalance() << endl;return 0;
}

我们使用上面的随机数来进行测试

运行结果如下所示

image-20230929223920622

六、 红黑树与AVL树的比较

红黑树和AVL树都是高效的平衡二叉树,增删改查的时间复杂度都是logN,红黑树不追求绝对平衡,其只需保证最长路径不超过最短路径的2倍,相对而言,降低了插入和旋转的次数,所以在经常进行增删的结构中性能比AVL树更优,而且红黑树实现比较简单,所以实际运用中红黑树更多。

从源代码中我们也可以看出来,其实map与set的底层都是红黑树,而且是kv结构的红黑树

image-20230929224532662

image-20230929224609714

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/144467.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于SSM+Vue的医院住院综合服务管理系统的设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用Vue技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

ROS系统读取USB相机图像数据

ROS系统读取USB相机图像数据 前言usb_cam 功能包下载与编译摄像头选择连接摄像头可配置参数 前言 usb_cam功能包简介 为了丰富机器人与外界的交互方式&#xff0c;已经增加了与机器人的语音交互方式&#xff0c;不仅使机器人能够说话发声&#xff0c;还能听懂我们说的话&#…

Windows中实现将bat或exe文件作为服务_且实现命令行安装、配置、启动、删除服务

一、背景描述 在Windows环境下进行日常的项目开发过程中&#xff0c;有时候需要将bat文件或exe文件程序注册为Windows的服务实现开机自己运行&#xff08;没有用户登陆&#xff0c;服务在开机后也可以照常运行&#xff09;、且对于那些没有用户交互界面的exe程序来说只要在后台…

STL upper_bound和lower_bound函数

声明&#xff1a; 首先包含头文件#include<algorithm> 这里的两个函数所运用的对象必须是非递减的序列&#xff08;也就是数组&#xff0c;数组必须是非递减的&#xff09;&#xff0c;只有这样才可以使用upper_bound和lower_bound这两个函数。 还有一点&#xff0c;就…

Python入门教程48:Pycharm永久镜像源的pip配置方法

国内几个好用的Python镜像服务器地址&#xff1a; 清华大学镜像站&#xff1a;https://pypi.tuna.tsinghua.edu.cn/simple/阿里云镜像站&#xff1a;https://mirrors.aliyun.com/pypi/simple/中科大镜像站&#xff1a;https://pypi.mirrors.ustc.edu.cn/simple/中国科技大学镜…

HTML5福利篇--使用Canvas画图

目录 一.Canvas元素 1.Canvas元素定义 2.使用JavaScript获取页面中的Canvas对象 二.绘制图形 1.绘制直线 2.绘制矩形 &#xff08;1&#xff09;rect() &#xff08;2&#xff09;strokeRect() &#xff08;3&#xff09;fillRect()和clearRect()函数 3.绘制圆弧 4.…

ElementUI之首页导航+左侧菜单->mockjs,总线

mockjs总线 1.mockjs 什么是Mock.js 前后端分离开发开发过程当中&#xff0c;经常会遇到以下几个尴尬的场景&#xff1a; - 老大&#xff0c;接口文档还没输出&#xff0c;我的好多活干不下去啊&#xff01; - 后端小哥&#xff0c;接口写好了没&#xff0c;我要测试啊&#x…

opencv: 解决保存视频失败的问题

摘要&#xff1a;opencv能读取视频&#xff0c;但保存视频时报错。 一、首先要确保已经下载了openh264.dll文件&#xff0c;否则保存的视频无法打开&#xff0c;详细可以浏览这个&#xff1a;opencv&#xff1a;保存视频。 二、保存视频时出现一下问题&#xff1a; OpenCV:…

COCO 数据集 人体关键点格式

图片与标注数据在 COCO官网 均提供下载 标注格式 COCO 的标注中包含 4 个部分/字段&#xff0c;"info" 描述数据集&#xff0c;"licenses" 描述图片来源&#xff0c;"images" 和 "annotations" 是主体部分 "images" 部分…

一文带你全面解析postman工具的使用

写在前面&#xff1a;本文转自今日头条作者雨滴测试&#xff0c;感兴趣可点击下方链接查看原文 基础篇效率篇高级篇 一文带你全面解析postman工具的使用 文章目录 一文带你全面解析postman工具的使用基础篇一、postman安装说明1.下载与安装2.界面导航说明3.发送第一个请求 二、…

3 OpenCV两张图片实现稀疏点云的生成

前文&#xff1a; 1 基于SIFT图像特征识别的匹配方法比较与实现 2 OpenCV实现的F矩阵RANSAC原理与实践 1 E矩阵 1.1 由F到E E K T ∗ F ∗ K E K^T * F * K EKT∗F∗K E 矩阵可以直接通过之前算好的 F 矩阵与相机内参 K 矩阵获得 Mat E K.t() * F * K;相机内参获得的方式…

87、Redis 的 value 所支持的数据类型(String、List、Set、Zset、Hash)---->List相关命令

本次讲解要点&#xff1a; List相关命令&#xff1a;是指value中的数据类型 启动redis服务器&#xff1a; 打开小黑窗&#xff1a; C:\Users\JH>e: E:>cd E:\install\Redis6.0\Redis-x64-6.0.14\bin E:\install\Redis6.0\Redis-x64-6.0.14\bin>redis-server.exe redi…

Java编码技巧:验证码

目录 1.1、EasyCaptcha&#xff08;优选&#xff0c;支持种类多&#xff0c;样式多&#xff0c;使用简单&#xff09;1.1.1、作用1.1.2、官方信息1.1.3、使用案例1.1.4、依赖1.1.5、代码1.1.6、效果1.1.7、拓展 1.2、kaptcha1.2.1、作用1.2.2、官方信息1.2.3、使用案例1.2.4、依…

DE0开发板交通灯十字路口红绿灯VHDL

名称&#xff1a;基于DE0开发板的交通灯十字路口红绿灯 软件&#xff1a;Quartus 语言&#xff1a;VHDL 要求&#xff1a; 设计一个十字路口交通信号灯的控制电路。分为两种情况&#xff0c;正常状态和报警状态。 1.正常状态&#xff1a;要求红、绿灯按一定的规律亮和灭&a…

触觉智能 PurPle Pi OH(OpenHarmony)开发板

资料汇总 内容预览 产品介绍 PurPle-Pi OH 规格书​​​​​​ 系统编译 Purple-Pi-OH Linux SDK编译 Purple-Pi-OH OHOS SDK编译 使用手册 Purple-Pi-OH Ubuntu系统使用手册 常见FAQ 常见问题 官网 官网地址 Purple Pi OH介绍 Purple Pi OH作为一款兼容树莓派的开…

多个pdf合并成一个文件,3个方法合并pdf

如何把多个pdf合并成一个文件&#xff1f;在我们日常的工作中&#xff0c;经常会遇到一些需要处理的文件&#xff0c;其中包括PDF文件。特别是当我们需要将多个PDF文件合并成一个PDF文件时&#xff0c;会面临一些困难。这样的情况下&#xff0c;我们的阅读能力会受到限制&#…

软件测试中的测试工具和自动化测试

1. 测试工具 测试工具也分为不同人员使用的 开发人员&#xff1a;测试框架&#xff0c;编写测试用例&#xff1b;各类线上dump分析工具如windgb&#xff1b;开发时的集成IDE工具如Visual Studio&#xff0c;idea等等 面向不同测试需求的测试工具 软件测试是软件开发生命周期…

手摸手带你 在Windows系统中安装Istio

Istio简介 通过负载均衡、服务间的身份验证、监控等方法&#xff0c;Istio 可以轻松地创建一个已经部署了服务的网络&#xff0c;而服务的代码只需很少更改甚至无需更改。 通过在整个环境中部署一个特殊的 sidecar 代理为服务添加 Istio 的支持&#xff0c;而代理会拦截微服务…

Multisim14.0仿真(二十七)基于UC3842的反激式开关电源的设计及仿真

一、UC3842简介&#xff1a; UC3842为固定频率电流模式PWM控制器。它们是专门为OFF−线和直流到直流转换器应用与最小的外部组件。内部实现的电路包括用于精确占空比控制的修剪振荡器、温度补偿参考、高增益误差放大器、电流传感比较器和理想适合于驱动功率MOSFET的高电流温度极…

MySQL学习笔记24

MySQL的物理备份&#xff1a; xtrabackup备份介绍&#xff1a; xtrabackup优缺点&#xff1a; 优点&#xff1a; 1、备份过程快速、可靠&#xff08;因为是物理备份&#xff09;&#xff1b;直接拷贝物理文件。 2、支持增量备份&#xff0c;更为灵活&#xff1b; 3、备份…