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

数据结构(六)——红黑树及模拟实现

目录

前言

红黑树的概念及性质

红黑树的效率

红黑树的结构

红黑树的插入

变色不旋转

单旋+变色

双旋+变色

插入代码如下所示:

红黑树的查找

红黑树的验证

红黑树代码如下所示:

小结


前言

在前面的文章我们介绍了AVL这一棵完全二叉搜索树,我们分析后发现AVL树查找的时间复杂度是O(logN),说明这是一个很优秀的数据结构,但是在底层实现我们发现,AVL树为了维持它的性质,是需要进行频繁的旋转的,这样会造成不必要的消耗,那么有没有一种数据结构既可保证它的时间复杂度为O(logN),又能避免频繁的旋转呢?这就是我们今天要介绍的红黑树,学习了红黑树之后我们将用红黑树作为底层,去封装实现一个模拟的map和set,下面让我们一起来学习吧。

参考文章:

据结构(五)——AVL树(平衡二叉搜索树)

红黑树的概念及性质

首先我们要知道什么是红黑树,与AVL树相比,红黑树的节点多了一个标志符号,这个标志符号表示节点的颜色,我们这里用枚举来表示,通过控制节点的颜色来判平衡以及判断是否需要旋转。由于红黑树的旋转不像AVL树那么频繁,所以它是一颗不完全平衡二叉搜索树。

下面我们引入路径的概念,我们把从根节点开始,一直到空节点的一条路线称为路径

它的规则如下所示:

规则一:根节点必须是黑色,其余节点的颜色不是黑色就是红色

规则二:每个红色节点的左右子树必须是红色的,即:不存在连续的红色节点

规则三:每条路径的黑色节点数量是相同的,其中我们把空节点视为黑色节点

规则四:最长路径的节点数量不超过最长路径节点数量的二倍

分析上面的规则我们发现,其中最关键也是最难维护的就是规则三,我们发现,在极端场景下,最短路径就是全为黑色节点的路径,最长路径就是红黑间隔的路径,所以维护好了规则三,规则四自然就实现了

红黑树的效率

由于有规则四的存在,我们设红黑树最短路径的节点数量是h,那么最长路径的节点数量就是2*h,我们设每条路径上黑色节点的数量是N,那么他们应该满足:2^{h}-1<=N<2^{2*h}-1,也就是说,如果我们要在红黑树当中进行增删查改,最少要走的长度是2^{h}-1,查找的次数就是log(N),最坏也就是查找2*log(N),分析下来,它的时间复杂度还是log(N)。

红黑树的结构

从上面的分析中我们知道,红黑树的大致框架与AVL树相似,只不过将平衡因子改成了用枚举表示的颜色,后续我们也是通过控制颜色来判平衡,因此红黑树的结构如下所示:

enum Colour
{BLACK,RED
};
template<class K, class V>
struct RBTreeNode
{pair<K, V> _kv;RBTreeNode<K, V>* _left;RBTreeNode<K, V>* _right;RBTreeNode<K, V>* _parent;Colour _col;RBTreeNode(const pair<K, V>& kv):_kv(kv), _left(nullptr), _right(nullptr), _parent(nullptr){}
};template<class K, class V>
class RBTree
{typedef RBTreeNode<K, V> Node;
public:
private:Node* _root = nullptr;
};

红黑树的插入

我们来分析红黑树的插入过程,首先由于它是一颗搜索树,所以按照搜索树的规则进行插入,更改插入结点的颜色,然后我们向上进行调整。

对于插入节点的颜色,我们发现,如果我们插入黑色,那么就在当前路径上增加了一个黑色节点,不符合每条路径上黑色节点数量相同这一规则,所以我们插入节点的颜色应该为红色。代码如下所示:

bool Insert(const pair<K, V>& kv)
{if (_root == nullptr){_root = new Node(kv);_root->_col = 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{cout << "插入值已经存在,请重新插入" << endl;return false;}}cur = new Node(kv);cur->_col = RED;if (parent->_kv.first < kv.first){parent->_right = cur;}else{parent->_left = cur;}cur->_parent = parent;
}

当我们插入红色节点后,会出下面几种情况:

情况一:父节点为黑色,不违反红黑树的规则,那么就停止向上调整

情况二:父节点为红色,违反规则,那么就需要向上调整

接下来我们重点分析情况二。由于不能有两个连续的红色节点,所以我们应当把父节点变成黑色,但是将父节点变成黑色后我们就在当前路径上凭空增加了一个黑色节点,这样破坏了黑色节点数量相同这一规则,那么我们就需要调整其他路径上黑色节点的数量,如果是这样的话,是不符合我们的预期的,我们再仔细观察红黑树的结构,我们把父节点的父节点定义为grandfather(祖父节点),那么祖父节点的另一个子节点称为uncle(叔节点),我们发现,无论什么情况,只要涉及到需要向上调整,祖父节点总是黑色的,此时,如果叔节点存在且为红色的话,我们就只需要将父节点和叔节点颜色变为黑色,再将祖父节点变为红色,这样就解决了这个问题;如果叔节点不存在或者存在且为空节点,那么此时我们就需要做特殊处理。

通过上述分析,我们总结一下红黑树的插入过程:

第一步:插入节点。按照二叉搜索树的规则插入节点,链接父节点,将插入节点的颜色改成红色,如果是根节点,就变为黑色。

第二步:向上调整黑色节点的数量,让其满足红黑树的规则。

接下来我们逐步来分析向上调整。

根据上面的分析我们知道,我们可以根据uncle节点的有无以及颜色,可以分为下面两种情况:

情况一:uncle节点存在且颜色为红色

情况二:uncle节点存在且为黑色或者uncle不存在

如下图所示:

对于情况一,我们只需要将父节点和uncle节点颜色变为黑色,然后再将祖父节点颜色变为红色,然后向上更新parent节点和grandfather节点,因此我们需要一个循环,循环结束的条件是:parent为空或者parent的颜色为黑色。

对于情况二,我们发现这种情况又需要分为两种场景,一种是插入节点是parent的左节点,另一种是插入节点是parent的右节点,这两种情景无论我们怎么变色都没有办法满足红黑树的规则,为此我们需要寻找一种新的解决办法。

回到红黑树的定义,我们知道,红黑树是一颗不完全平衡二叉搜索树,从前面的AVL树我们知道,要成为一颗平衡树,我们要做的就是旋转,而旋转分为左旋和右旋,那么我们尝试将AVL树的旋转带入红黑树当中,那么对于parent与cur都位于同一侧节点的这种情况,我们使用单旋,对于parent与cur位于不同一侧节点的这种情况,我们使用双旋。

综上所述,红黑树的插入可以分为以下三种不同的情况:

变色不旋转

条件:grandfather为黑,parent与cur为红色,uncle存在且为红。

解决办法:parent与cur变为黑色,grandfather变为红色,然后再将grandfather当成新的cur,继续向上更新。

分析:由于cur与parent都是红色,所以我们应该把parent变为黑色,这样就在当前节点凭空增加了一个黑色节点,从而导致黑色节点失衡;由于uncle是红色,为了让黑色节点数量保持平衡,所以我们需要把uncle也变成黑色,然后让grandfather变为红色。

单旋+变色

条件:grandfather为黑色,parent与cur为红色,uncle不存在或者uncle存在且为黑,并且parent与cur位于同一侧

解决办法:以grandfather节点为旋转点,如果位于左侧则右旋;如果位于右侧则左旋,旋转结束后让parent变为黑色,grandfather变成红色

分析:由于uncle是黑色节点,如果我们直接按照第一种情况进行变色,那么就会导致uncle这条路径上始终少一个节点,这样导致节点失衡,所以我们要先进行旋转,旋转结束后parent成为了cur与grandfather的父节点,为了让黑色节点数量保持平衡,我们需要让parent变成黑色,让grandfather变成红色

双旋+变色

条件:grandfather为黑色,parent与cur为红色,uncle不存在或者uncle存在且为黑,并且parent与cur位于不同侧

解决办法:首先以parent为旋转点进行一次旋转,然后再以grandfather为旋转点进行一次旋转,旋转结束后让cur变成黑色,让grandfather变成红色。

分析:由于uncle是黑节点,cur与parent位于相反的两侧,如果我们旋转一次,结果还是这种情况,所以我们需要进行两次旋转,旋转结束后,为了保持黑色节点数量的平衡,我们需要将cur变成黑色,让grandfather变成红色

插入代码如下所示:

bool Insert(const pair<K, V>& kv)
{if (_root == nullptr){_root = new Node(kv);_root->_col = 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{cout << "插入值已经存在,请重新插入" << endl;return false;}}cur = new Node(kv);cur->_col = RED;if (parent->_kv.first < kv.first){parent->_right = cur;}else{parent->_left = cur;}cur->_parent = parent;while (parent && parent->_col == RED){Node* grandfather = parent->_parent;if (grandfather->_left == parent){Node* uncle = grandfather->_right;if (uncle && uncle->_col == RED){parent->_col = uncle->_col = BLACK;grandfather->_col = RED;cur = grandfather;parent = cur->_parent;}else{if (parent->_left == cur){RotateR(grandfather);grandfather->_col = RED;parent->_col = BLACK;}else{RotateL(parent);RotateR(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}else{Node* uncle = grandfather->_left;if (uncle && uncle->_col == RED){parent->_col = uncle->_col = BLACK;grandfather->_col = RED;cur = grandfather;parent = cur->_parent;}else{if (parent->_right == cur){RotateL(grandfather);grandfather->_col = RED;parent->_col = BLACK;}else{RotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}}_root->_col = BLACK;return true;
}
void RotateL(Node* parent)
{Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;subR->_left = parent;if (subRL)subRL->_parent = parent;Node* grandfather = parent->_parent;parent->_parent = subR;if (_root == parent){_root = subR;subR->_parent = nullptr;}else{if (grandfather->_left == parent){grandfather->_left = subR;}else{grandfather->_right = subR;}subR->_parent = grandfather;}}
void RotateR(Node* parent)
{Node* subL = parent->_left;Node* subLR = subL->_right;parent->_left = subLR;subL->_right = parent;if (subLR)subLR->_parent = parent;Node* grandfather = parent->_parent;parent->_parent = subL;if (_root == parent){_root = subL;subL->_parent = nullptr;}else{if (grandfather->_left == parent){grandfather->_left = subL;}else{grandfather->_right = subL;}subL->_parent = grandfather;}
}

旋转逻辑在这篇文章介绍:

数据结构(五)——AVL树(平衡二叉搜索树)

红黑树的查找

红黑树的查找与二叉搜索树相同,都是通过遍历比较节点值与目标值的大小知道找到目标值所在的节点然后返回或者直到遍历完这颗树为止,其代码如下所示:

Node* Find(const pair<K, V>& kv)
{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 cur;}}cout << "目标值不存在" << endl;return nullptr;
}

红黑树的验证

前面我们验证红黑树时使用的方法是验证平衡因子以及左右子树高度差,实际上我们是按照AVL树的规则进行验证的,那么对于红黑树而言我们要通过它的规则进行验证,检查最长路径不超过最短路径的二倍吗?这样显然是不现实的,那么我们是通过什么方法去验证呢?我们还是通过递归去验证,不同的是我们引入了一个新的参数——参考值,这个值是用来记录从根节点到空节点这一整条路径中黑色节点的数量的,我们用这个值作为参考去比较其他路径上黑色节点的数量是否与这个值相同。其代码如下所示:

bool Check(Node* root, int blackNum, const int refNum)
{if (root == nullptr){// 前序遍历⾛到空时,意味着⼀条路径⾛完了//cout << blackNum << endl;if (refNum != blackNum){cout << "存在⿊⾊结点的数量不相等的路径" << endl;return false;}return true;}// 检查孩⼦不太⽅便,因为孩⼦有两个,且不⼀定存在,反过来检查⽗亲就⽅便多了if (root->_col == RED && root->_parent->_col == RED){cout << root->_kv.first << "存在连续的红⾊结点" << endl;return false;}if (root->_col == BLACK){blackNum++;}return Check(root->_left, blackNum, refNum)&& Check(root->_right, blackNum, refNum);
}
bool IsBalance()
{if (_root == nullptr)return true;if (_root->_col == RED)return false;// 参考值int refNum = 0;Node* cur = _root;while (cur){if (cur->_col == BLACK){++refNum;}cur = cur->_left;}return Check(_root, 0, refNum);
}

红黑树代码如下所示:

enum Colour
{BLACK,RED
};
template<class K, class V>
struct RBTreeNode
{pair<K, V> _kv;RBTreeNode<K, V>* _left;RBTreeNode<K, V>* _right;RBTreeNode<K, V>* _parent;Colour _col;RBTreeNode(const pair<K, V>& kv):_kv(kv), _left(nullptr), _right(nullptr), _parent(nullptr){}
};template<class K, class V>
class RBTree
{typedef RBTreeNode<K, V> Node;
public:bool Insert(const pair<K, V>& kv){if (_root == nullptr){_root = new Node(kv);_root->_col = 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{cout << "插入值已经存在,请重新插入" << endl;return false;}}cur = new Node(kv);cur->_col = RED;if (parent->_kv.first < kv.first){parent->_right = cur;}else{parent->_left = cur;}cur->_parent = parent;while (parent && parent->_col == RED){Node* grandfather = parent->_parent;if (grandfather->_left == parent){Node* uncle = grandfather->_right;if (uncle && uncle->_col == RED){parent->_col = uncle->_col = BLACK;grandfather->_col = RED;cur = grandfather;parent = cur->_parent;}else{if (parent->_left == cur){RotateR(grandfather);grandfather->_col = RED;parent->_col = BLACK;}else{RotateL(parent);RotateR(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}else{Node* uncle = grandfather->_left;if (uncle && uncle->_col == RED){parent->_col = uncle->_col = BLACK;grandfather->_col = RED;cur = grandfather;parent = cur->_parent;}else{if (parent->_right == cur){RotateL(grandfather);grandfather->_col = RED;parent->_col = BLACK;}else{RotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}}_root->_col = BLACK;return true;}void RotateL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;subR->_left = parent;if (subRL)subRL->_parent = parent;Node* grandfather = parent->_parent;parent->_parent = subR;if (_root == parent){_root = subR;subR->_parent = nullptr;}else{if (grandfather->_left == parent){grandfather->_left = subR;}else{grandfather->_right = subR;}subR->_parent = grandfather;}}void RotateR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;parent->_left = subLR;subL->_right = parent;if (subLR)subLR->_parent = parent;Node* grandfather = parent->_parent;parent->_parent = subL;if (_root == parent){_root = subL;subL->_parent = nullptr;}else{if (grandfather->_left == parent){grandfather->_left = subL;}else{grandfather->_right = subL;}subL->_parent = grandfather;}}Node* Find(const pair<K, V>& kv){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 cur;}}cout << "目标值不存在" << endl;return nullptr;}bool Check(Node* root, int blackNum, const int refNum){if (root == nullptr){// 前序遍历⾛到空时,意味着⼀条路径⾛完了//cout << blackNum << endl;if (refNum != blackNum){cout << "存在⿊⾊结点的数量不相等的路径" << endl;return false;}return true;}// 检查孩⼦不太⽅便,因为孩⼦有两个,且不⼀定存在,反过来检查⽗亲就⽅便多了if (root->_col == RED && root->_parent->_col == RED){cout << root->_kv.first << "存在连续的红⾊结点" << endl;return false;}if (root->_col == BLACK){blackNum++;}return Check(root->_left, blackNum, refNum)&& Check(root->_right, blackNum, refNum);}bool IsBalance(){if (_root == nullptr)return true;if (_root->_col == RED)return false;// 参考值int refNum = 0;Node* cur = _root;while (cur){if (cur->_col == BLACK){++refNum;}cur = cur->_left;}return Check(_root, 0, refNum);}
private:Node* _root = nullptr;
};

小结

本篇文章我们着重介绍了红黑树的概念以及插入逻辑的实现,红黑树作为一颗不完全平衡二叉搜索树有着独特的地方,它是通过节点的颜色来判断是否平衡以及是否需要旋转操作,它省去了频繁旋转所带来的消耗,从而更加追求效率,总的来说,红黑树是一颗非常优秀的数据结构

以上就是本篇博客的主要内容,如果对您有所帮助的话,记得点赞、评论、关注加转发,您的支持就是我创作的最大动力

相关文章:

数据结构(六)——红黑树及模拟实现

目录 前言 红黑树的概念及性质 红黑树的效率 红黑树的结构 红黑树的插入 变色不旋转 单旋变色 双旋变色 插入代码如下所示&#xff1a; 红黑树的查找 红黑树的验证 红黑树代码如下所示&#xff1a; 小结 前言 在前面的文章我们介绍了AVL这一棵完全二叉搜索树&…...

【Linux】基础 IO(文件描述符、重定向、缓冲区)

Linux 1.理解文件2.C文件接口1.打开 写文件2.读文件 简单实现cat命令3.输出信息到显示器的方式4.stdin、stdout、stderr5.打开文件的方式 3.系统接口 IO1.传递标志位2.open、close3.write、read 4.文件描述符1.是什么&#xff1f;2.分配规则3.重定向原理4.通过dup2系统调用重…...

记录一下远程调试 备忘

‌在进行远程调试时&#xff0c;目标主机不需要安装完整的编程环境‌(舍去重复安装)。可以使用Visual Studio的远程调试功能&#xff0c;或者使用gdb和gdbserver进行远程调试。 Visual Studio远程调试 ‌复制远程调试器文件夹‌&#xff1a;将Visual Studio安装目录下的remot…...

libevent服务器附带qt界面开发(附带源码)

本章是入门章节&#xff0c;讲解如何实现一个附带界面的服务器&#xff0c;后续会完善与优化 使用qt编译libevent源码演示视频qt的一些知识 1.主要功能有登录界面 2.基于libevent实现的服务器的业务功能 使用qt编译libevent 下载这个&#xff0c;其他版本也可以 主要是github上…...

MyISAM索引方案

在InnoDB中索引即数据&#xff0c;也就是聚簇索引的B树叶子节点已经包含了所有完整的用户记录&#xff0c;MyISAM的索引方案虽然也是树形结构&#xff0c;但是将索引和数据分开存储 将表中的记录按记录的插入顺序单独存储在一个文件中【数据文件】&#xff0c;这个文件不划分数…...

Windows 图形显示驱动开发-WDDM 1.2功能—WDDM 1.2 中的 Direct3D 功能和要求

一、架构演进与驱动模型 1.1 WDDM驱动模型的革命性升级 Windows 8引入的WDDM 1.2驱动模型在以下方面实现突破&#xff1a; 内存管理&#xff1a;采用统一虚拟地址空间&#xff08;UVA&#xff09;架构&#xff0c;使CPU和GPU可共享相同的指针地址空间。具体实现通过DXGK_DRI…...

深度解析 Vue 项目 Webpack 分包与合包 一文读懂

深度解析 Vue 项目 Webpack 分包与合包 一文读懂 文章目录 深度解析 Vue 项目 Webpack 分包与合包 一文读懂一、Webpack 打包机制深度解析1.1 模块化系统的本质1.2 Webpack 构建流程解析1.3 默认打包的问题分析 二、分包策略深度配置2.1 SplitChunksPlugin 核心配置2.2 精细化分…...

【ROS】map_server 地图的保存和加载

【ROS】map_server 地图的保存和加载 前言地图的保存地图的加载 前言 在 ROS 中&#xff0c;想要实现导航功能&#xff0c;首先需要一张已建好的地图。导航系统依赖这张地图进行路径规划、定位和障碍物避让等操作。本文将讲解在使用 gmapping 或 hector_mapping 建图后&#x…...

【计网】SSL/TLS核心原理

序言 在HTTP协议中&#xff0c;信息是明文传输的&#xff0c;因此为了通信安全就有了HTTPS(Hyper Text Transfer Protocol over Secure Socket Layer)协议。HTTPS也是一种超文本传送协议&#xff0c;在HTTP的基础上加入了SSL/TLS协议&#xff0c;SSL/TLS依靠证书来验证服务端的…...

sqli-labs靶场 less 11

文章目录 sqli-labs靶场less 11 POS联合注入 sqli-labs靶场 每道题都从以下模板讲解&#xff0c;并且每个步骤都有图片&#xff0c;清晰明了&#xff0c;便于复盘。 sql注入的基本步骤 注入点注入类型 字符型&#xff1a;判断闭合方式 &#xff08;‘、"、’、“”&…...

陕化之光(原创)

当城市在和周公化合 陕化的工装已与朝霞发生反应 工人先锋号已然吹响 陕化工人游走在工作的床层 钢铁森林间穿梭的身影 是沉默的催化剂 让冰冷的方程式 绽放出最活跃的分子温度 扳手与阀门对话时 塔林正在记录 关于电流与压力的学习笔记 每一次精确的调控 都是舞台上…...

【刷题2025】高级数据结构(并查集+优先队列+图论)

1.并查集 (1)基础理论 并查集是一种树形的数据结构,用于处理一些不相交集合的 合并 及 查询 问题。比如,可以用并查集判断一个森林中有几棵树、某个节点是否属于某棵树。 并查集由一个整形数组 pre[] 和两个函数 find() 、 join() 构成。 数组 pre[] 记录了每个点的前驱…...

数据库性能优化(sql优化)_分布式优化思路01_yxy

数据库性能优化_分布式优化思路01 1 分布式数据库的独特挑战2 分布式新增操作符介绍2.1 数据交换操作符(ESEND/ERECV):2.2 数据迭代操作符GI:3 核心优化策略(一)_分区裁剪优化3.1 普通分区裁剪3.2 动态分区裁剪1 分布式数据库的独特挑战 在分布式数据库系统中,核心为数据被…...

云服务器和物理服务器有什么区别

云服务器与物理服务器的核心区别在于资源分配方式、性能稳定性、成本结构、运维管理及 适用场景。以下是具体分析: 一、资源分配与架构差异 云服务器:基于虚拟化技术,将物理服务器集群分割为多个虚拟实例&#xff0c;资源由多个用户 共享&#xff0c;可根据需求弹性调整配置…...

FPGA-DDS技术的波形发生器

1.实验目的 1.1掌握直接数字频率合成&#xff08;DDS&#xff09;的基本原理及其实现方法。 1.2在DE2-115 FPGA开发板上设计一个可调频率的正弦波和方波发生器&#xff0c;频率范围10Hz~5MHz&#xff0c;最小分辨率小于1kHz。 1.3使用Quartus II进行仿真&#xff0c;并通过S…...

晶晨线刷工具下载及易错点说明:Key文件配置错误/mac剩余数为0解决方法

晶晨线刷工具下载及易错点说明&#xff1a;Key文件配置错误&#xff0f;mac剩余数为0解决方法 各种版本晶晨线刷工具下载&#xff1a; 晶晨线刷工具易出错点故障解决方法&#xff1a; 1、晶晨线刷工具加载固件的时候提示mac红字且剩余数为0的解决办法 很多同学可能会与遇到加…...

idea如何使用git

在 IntelliJ IDEA 中使用 Git 的详细步骤如下&#xff0c;分为配置、基础操作和高级功能&#xff0c;适合新手快速上手&#xff1a; ​一、配置 Git​ ​安装 Git​ 下载并安装 Git&#xff0c;安装时勾选“Add to PATH”。验证安装&#xff1a;终端输入 git --version 显示版本…...

python——学生管理系统

学生管理系统主要分为以下三个大类&#xff1a; 一、用户类&#xff08;User&#xff09;&#xff1a; 属性&#xff1a;用户名&#xff08;username&#xff09;、密码&#xff08;password&#xff09; 功能&#xff1a;注册&#xff08;register&#xff09;、登录&#…...

快速幂+公共父节点

# 快速幂 求&#xff1a;23的10000次幂&#xff0c;那么就是求23的5000次幂&#xff0c;因为2350*235023^100;所以可以遍历log(n)次 int res1; int tmp23; for(int i1;i<logn;i) {tmp*tmp; }显然&#xff0c;我们无法通过logn计算次数&#xff1b; 比如是非偶数的怎么计算呢…...

【差分隐私相关概念】瑞丽差分隐私(RDP)命题4

命题4的证明详解&#xff08;分情况讨论&#xff09; 背景与设定 机制&#xff1a; f : D → R f: \mathcal{D} \to \mathcal{R} f:D→R 是由 n n n 个 ϵ \epsilon ϵ-差分隐私机制自适应组合而成。相邻输入&#xff1a; D D D 和 D ′ D D′ 是相邻数据集。目标&#xf…...

Vue 人看 React useRef:它不只是替代 ref

如果你是从 Vue 转到 React 的开发者&#xff0c;初见 useRef 可能会想&#xff1a;这不就是 React 版的 ref 吗&#xff1f;但真相是 —— 它能做的&#xff0c;比你想象得多得多。 &#x1f440; Vue 人初见 useRef 在 Vue 中&#xff0c;ref 是我们访问 DOM 或响应式数据的…...

C++第三方库【JSON】nlohman/json

文章目录 优势使用API从文件中读取json从json文本创建json对象直接创建并操作json对象字符串 <> json对象文件流 <> json对象从迭代器读取像使用STL一样的访问STL容器转化为 json数组STL容器 转 json对象自定义类型转化为 json对象 限制 优势 直观的语法&#xff…...

从源码到实战:深度解析`rsync`增量同步机制与高级应用

从源码到实战&#xff1a;深度解析rsync增量同步机制与高级应用 #mermaid-svg-C1ZMwvhtq4iP4E8m {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-C1ZMwvhtq4iP4E8m .error-icon{fill:#552222;}#mermaid-svg-C1ZMwvht…...

数据库表设计五层分类系统表设计

文章目录 数据库表设计五层分类系统表设计代码思路详解类概述核心方法详解1. processString(String input) 方法2. createNo(String input, boolean peerNode) 方法3. isParent(String parentNo, String sonNo) 方法 编号系统设计使用场景推测代码特点可能的使用示例 NoProcess…...

Centos/RedHat 7.x服务器挂载ISCSI存储示例(无多路径非LVM)

客户让帮忙挂载个ISCSI存储&#xff0c;大概结构如下图所示&#xff1a; ISCSI存储为一台安装了truenas的X86服务器&#xff0c;提供存储服务的IP地址为10.16.0.1 服务器的ETH1网卡配置与10.16.0.1同段网络。 为了给客户做个简单培训&#xff0c;整理了一下操作步骤。下面是配…...

【android bluetooth 协议分析 21】【ble 介绍 2】【什么是IRK,是如何生成和传递的】

1. 什么是 IRK&#xff1f; IRK&#xff0c;全称 Identity Resolving Key&#xff08;身份解析密钥&#xff09;&#xff0c;是 BLE 设备用于生成和解析 Resolvable Private Address&#xff08;RPA&#xff09; 的密钥。 2. IRK 的生成和传递过程 IRK 是在 BLE 配对&#xf…...

4.14-4.15学习总结 IO流:缓冲流+转换流+序列化流+打印流+压缩流+Commons—io工具包+Hutool工具包

图片加密操作&#xff1a; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; public class test {public static void main(String[] args) throws IOException {FileInputStream fisnull;FileOutputStream fosnull;try{fisnew…...

Linux入门学习笔记

一、文件路径相关 相对路径与绝对路径 相对路径&#xff1a;是从当前工作目录开始表示文件或目录位置的路径。例如&#xff0c;当前在 /home/user 目录下&#xff0c;若要访问 user 目录下 test 文件夹中的 file.txt 文件&#xff0c;相对路径就是 test/file.txt 。它依赖于当…...

Redis适用场景

Redis适用场景 一、加速缓存二、会话管理三、排行榜和计数器四、消息队列五、实时分析六、分布式锁七、地理位置数据八、限流九、数据共享十、签到 一、加速缓存 Redis最常见的应用之一是作为缓存层&#xff0c;用于存储频繁访问的数据&#xff0c;从而减轻数据库的负载。 通过…...

# WPS打开新文档,“工具”菜单下是空白

WPS打开新文档&#xff0c;“工具”菜单下是空白 在 WPS 中打开新文档后 “工具” 菜单下空白&#xff0c;可能由多种原因导致&#xff0c;如下图&#xff1a; 下面分析并给出对应的解决办法&#xff1a; 一、 功能区显示设置问题 1、原因&#xff1a; WPS 的功能区显示可能…...

【软考-架构】13.3、架构复用-DSSA-ABSD

✨资料&文章更新✨ GitHub地址&#xff1a;https://github.com/tyronczt/system_architect 文章目录 1、软件架构复用2、特定领域软件架构DSSADSSA的三个基本活动参与DSSA的四种角色人员建立DSSA的过程三层次模型 考试真题第一题第二题 3、基于架构的软件开发ABSD的软件开发…...

K8S_ResourceQuota与LimitRange的作用

ResourceQuota 作用详解 资源总量控制&#xff1a;ResourceQuota能对命名空间内的资源使用总量进行限制。在一个Kubernetes集群中&#xff0c;存在多个命名空间&#xff0c;每个命名空间可看作一个独立的工作单元。通过设置ResourceQuota&#xff0c;可以防止某个命名空间过度…...

T101D加固平板电脑:无人机地面站的高效智能控制核心

随着无人机技术在应急救援、农业监测、军事侦察等领域的广泛应用&#xff0c;对地面控制设备的要求也日益提高。鲁成伟业推出的T101D加固平板电脑凭借其高性能、强防护和专业化设计&#xff0c;成为无人机地面站的核心控制终端&#xff0c;为复杂环境下的作业提供了可靠支持。 …...

LLM中的N-Gram、TF-IDF和Word embedding

文章目录 1. N-Gram和TF-IDF&#xff1a;通俗易懂的解析1.1 N-Gram&#xff1a;让AI学会"猜词"的技术1.1.1 基本概念1.1.2 工作原理1.1.3 常见类型1.1.4 应用场景1.1.5 优缺点 1.2 TF-IDF&#xff1a;衡量词语重要性的尺子1.2.1 基本概念1.2.2 计算公式1.2.3 为什么需…...

【基于Servlet技术处理表单】

文章目录 一、实验背景与目的二、实验设计与实现思路1. 功能架构2. 核心代码实现3. 测试用例 总结 一、实验背景与目的 本次实验旨在深入理解Servlet工作原理&#xff0c;掌握JSP与Servlet的协同开发&#xff0c;实现前端表单与后端数据处理的交互。具体目标包括&#xff1a;设…...

【差分隐私相关概念】瑞丽差分隐私(RDP)-瑞丽散度约束了贝叶斯因子后验变化

分步解释和答案&#xff1a; 在Rnyi差分隐私&#xff08;RDP&#xff09;框架中&#xff0c;通过贝叶斯因子和Rnyi散度的关系可以推导出关于后验变化的概率保证。以下是关键步骤的详细解释&#xff1a; 1. 贝叶斯因子的定义与分解 设相邻数据集 D D D 和 D ′ D D′&#x…...

Oracle查询大表的全部数据

2000w的大表 表结构如下&#xff0c;其中id是索引 查询处理慢的写法 List<String> queryLoidForPage(Integer startNum,Integer endNum){try {Connection oracleConnection initBean.oracleConnection;Statement stmt oracleConnection.createStatement();// 4.执行查…...

linux 内核 static-key机制分析

1、static key是什么 Linux内核的 Static Keys机制是一种高效的条件分支优化技术,主要用于在运行时动态启用或禁用特定代码路径,同时‌避免常规条件判断(如 if 语句)的性能开销‌。它通过结合编译时优化和运行时代码修补(如 Jump Label 技术)实现近乎零成本的开关切换,广泛应用…...

【Java学习】Knife4j使用流程

手动添加依赖&#xff0c;并刷新Maven <dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-openapi2-spring-boot-starter</artifactId><version>4.3.0</version> </dependency>在配置文件application.…...

Java 基本操作快速入门:理解与实践

在软件开发的世界里&#xff0c;Java 作为一种广泛使用的编程语言&#xff0c;已经成为构建企业级应用、移动应用甚至大型系统的主力军。对于任何一位初学者来说&#xff0c;理解 Java 的基本操作是学习编程的第一步。从变量声明到控制流的结构&#xff0c;每一个基础知识点都是…...

jetson orin nano 开发板conda 的 base 环境在 shell 启动时自动激活

使用MobaXterm_Personal_23.0.exe 连接jetson开发板时默认是不进入base环境的 1.输入此命令nano ~/.bashrc看到图1后把conda activate 你的环境名 放到图中标记位置 然后保存退出&#xff1a; Ctrl O 回车保存 Ctrl X 退出编辑器 输入此命令后&#xff0c;source ~/.bas…...

【高中数学/指数/对数】同构六君子之 x/e^x/lnx组合曲线

yx*e^x ye^x/x yx/e^x yx*lnx ylnx/x yx/lnx END...

Golang|在线排查协程泄漏

根据我们的代码&#xff0c;前5毫秒内&#xff0c;每隔1毫秒就会来一个请求&#xff0c;5毫秒之后由于前面的协程执行完&#xff0c;后面又会来新的协程&#xff0c;所以协程数目会保持稳定但是代码一运行&#xff0c;协程数量一直增长&#xff0c;发生了协程泄漏 我们可以list…...

健康养生指南

在快节奏的现代生活中&#xff0c;健康养生愈发重要&#xff0c;它是我们享受美好生活的基石。​ 饮食是养生的关键一环。秉持均衡原则&#xff0c;每日保证谷类、蔬果、优质蛋白等各类食物合理摄入。多吃富含膳食纤维的粗粮&#xff0c;像燕麦、糙米&#xff0c;可促进肠道蠕…...

实验二 两个多位十进制数相加实验

一、实验目的 1&#xff0e;掌握汇编子程序的编写方法。 2&#xff0e;掌握循环程序的设计方法。 二、实验内容 将键盘输入的两个5位十进制数相加&#xff0c;在屏幕上显示相加的结果。 三、实验要求 1&#xff0e;显示格式&#xff1a;被加数加数相加的结…...

多模态大模型MLLM基础训练范式 Pre-train + Instruction FineTuning

多模态大模型Pre-train 为了在图文嵌入空间中更好地对齐视觉和文本信息。为此&#xff0c;使用图像-文本对&#xff08;image-caption style data&#xff09;&#xff0c;表示为 ( X , Y a ) (\mathbf{X}, Y_a) (X,Ya​)&#xff0c;其中&#xff1a; X \mathbf{X} X&#x…...

2025.4.15六年之约day11

六年之约已经断更好几个月了&#xff0c;当初六年之约是当做日记来写的&#xff0c;然后被同事刷到了&#xff0c;被谈及的时候挺尴尬的&#xff0c;毕竟里面记录的是我的所思所想。在互联网下&#xff0c;是不适合发布日记的&#xff0c;但我又爱记录所思所想所做。 不知道距…...

Rust学习之实现命令行小工具minigrep(二)

Rust学习之实现命令行小工具minigrep&#xff08;二&#xff09; Rust学习之实现命令行小工具minigrep&#xff08;一&#xff09; 前言 继续记录一下Rust 语言学习过程&#xff0c;上次写了一个命令行查找字符串的小项目minigrep。学习完了闭包&#xff08;Closures&#x…...

Access Token 和 Refresh Token 的双令牌机制,维持登陆状态

目录 1. 双令牌机制2. 工作流程3. 客户端实现4. 服务器端实现5. 注意事项拓展&#xff1a;Token在客户端安全存储的几种方式 为了实现客户端在 JWT Token 过期后自动更新 Token&#xff0c;通常会采用 Access Token 和 Refresh Token 的双令牌机制。以下是实现自动更新 Token 的…...

前端 -- uni-app 的 splitChunks 分包详解与实战!

全文目录: 开篇语📝 前言📖 目录🌟 什么是 splitChunks?🛠 splitChunks 的核心原理📂 文件拆分的机制⚙️ 配置选项✨ splitChunks 实战案例1️⃣ 项目初始化2️⃣ 编写页面逻辑3️⃣ 配置 splitChunks4️⃣ 查看效果🧩 splitChunks 的高级用法与优化🔍 优化一…...