【C++进阶】四、红黑树(三)
目录
一、红黑树的概念
二、红黑树的性质
三、红黑树节点的定义
四、红黑树的插入
五、红黑树的验证
六、红黑树与AVL树的比较
七、完整代码
一、红黑树的概念
红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍,因而是接近平衡的
如下图就是一棵红黑树:
二、红黑树的性质
红黑树有以下性质:
- 每个结点不是红色就是黑色
- 根节点是黑色的
- 如果一个节点是红色的,则它的两个孩子结点是黑色的,即没有连续红色节点
- 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均包含相同数目的黑色结点
- 每个叶子结点都是黑色的(此处的叶子结点指的是空结点,如上图的NIL节点)
- 红黑树最优情况(左右平衡):全黑或每条路径都是一黑一红相间的满二叉树,搜索高度 logN
- 红黑树最差情况(左右极不平衡):每颗子树左子树全黑,右子树一黑一红,搜索高度 2*logN
红黑树不追求极致的平衡,AVL树则是追求极致的平衡,红黑树是近似平衡;红黑树这种近似平衡的结构大大减少了大量的旋转,红黑树的综合性能优于 AVL树
为什么红黑树满足上面的性质,红黑树就能保证:其最长路径中节点个数不会超过最短路径节点个数的两倍?
- 红黑树的最短路径:全黑,一条路径上的全是黑色节点
- 红黑树的最长路径:一黑一红相间的路径
比如:
三、红黑树节点的定义
红黑树也是使用键值对,即KV模型,也是为了方便后序操作,红黑树的结构也是三叉链,即增加了指向父节点的 parent指针,还增加了一个成员变量,用于标识节点的颜色(red or black)
enum Colour
{RED,BLACK,
};//K:key, V:value
template<class K, class V>
struct RBTreeNode
{//构造函数RBTreeNode(const pair<K, V>& kv):_kv(kv),_left(nullptr),_right(nullptr),_parent(nullptr),_col(RED){}//成员变量pair<K, V> _kv;RBTreeNode<K, V>* _left;RBTreeNode<K, V>* _right;RBTreeNode<K, V>* _parent;Colour _col;
};template<class K, class V>
class RBTree
{typedef RBTreeNode<K, V> Node;
public:private:Node* _root = nullptr;//缺省值
};
注:这里使用了枚举来列举颜色
为什么构造红黑树结点时,默认将结点的颜色设置为红色?
- 插入结点如果是黑色的,一定破坏红黑树的性质4,无论如何都必须对红黑树进行调整。
- 插入结点如果是红色的,可能破坏红黑树的性质3,可能需要对红黑树进行调整 或者不需要调整
所以将节点颜色默认设置为红色
四、红黑树的插入
红黑树的插入分两步:
- 按照二叉搜索树的方式插入新节点
- 判断是否需要对红黑树进行调整
(1)插入节点
因为红黑树本身就是一棵二叉搜索树,因此寻找结点的插入位置是非常简单的,按照二叉搜索树的插入规则:
- 待插入结点的key值比当前结点小就插入到该结点的左子树
- 待插入结点的key值比当前结点大就插入到该结点的右子树
- 待插入结点的key值与当前结点的 key 值相等就插入失败
(2)判断是否需要对红黑树进行调整
判断:插入节点的父亲 parent 存在且为红色,则需要进行调整,否则不需要
然后分两种情况:
- (A)parent在 grandfather 的左边
- (B)parent在 grandfather 的右边
注:进行调整的关键是 uncle
(A)parent在 grandfather 的左边有三种情况:
- 情况1:uncle存在且为红,uncle和parent的颜色需要修改为黑,granfather 修改为红,如果满足循环条件继续往上更新
- 情况2:uncle存在且为黑,需要对红黑树进行旋转
- 情况3:uncle不存在,需要对红黑树进行旋转
情况1,图如下:
注:情况2和情况3是一起处理的
情况2 + 情况3:
- cur,parent,grandfather 三个节点在一条直线上,单旋处理即可,对 grandfater 进行右单旋,然后 parent 的颜色改为黑,grandfater 的颜色改为红
- cur,parent,grandfather 三个节点是折线,需要双旋处理,对 parent 进行左单旋,然后对 grandfater 进行右单旋,然后 cur 的颜色改为黑,grandfater 的颜色改为红
情况2,图如下:
cur,parent,grandfather 三个节点在一条直线上
调颜色
cur,parent,grandfather 三个节点是折线
调颜色
情况3,图如下:
cur,parent,grandfather 三个节点在一条直线上
调颜色
cur,parent,grandfather 三个节点是折线
调颜色
(B)parent在 grandfater 的右边也有三种情况:(与左边情况完全一致,只是旋转不同)
- 情况1:uncle存在且为红,uncle和parent的颜色需要修改为黑,grandfater修改为红,如果满足循环条件继续往上更新
- 情况2:uncle存在且为黑,需要对红黑树进行旋转,对 grandfather 进行右单旋
- 情况3:uncle不存在,需要对红黑树进行双旋转,对 parent 进行左单旋,然后对 grandfather 进行右单旋
注:情况2和情况3是一起处理的
情况2 + 情况3:
- cur,parent,grandfather 三个节点在一条直线上,单旋处理即可,对 grandfather 进行左单旋,然后 parent 的颜色改为黑,grandfater 的颜色改为红
- cur,parent,grandfather 三个节点是折线,需要双旋处理,对 parent 进行右单旋,然后对 grandfather 进行左单旋,然后 cur 的颜色改为黑,grandfather 的颜色改为红
图就不画了,左边的图反过来就是右边的图,旋转在 AVL树有解释,这里就不再解释
经调整后,保持了红黑树的特性
插入代码如下:
//插入
bool Insert(const pair<K, V>& kv)
{//节点为空,新建根节点if (_root == nullptr){_root = new Node(kv);_root->_col = BLACK;//根节点默认为黑色return true;}//节点为不空Node* parent = nullptr;//用于记录上一个节点Node* cur = _root;//寻找合适的位置进行插入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//cur->kv.first == kv.first要插入值已经存在,插入失败{return false;}}cur = new Node(kv);cur->_col = RED;//新节点默认为红//插入if (parent->_kv.first < kv.first)//插入到parent左边{parent->_right = cur;cur->_parent = parent;}else//插入到parent右边{parent->_left = cur;cur->_parent = parent;}//进行调平衡 && 保持红黑树的特性,即插入节点的父亲是红色,需要对红黑树进行调整while (parent && parent->_col == RED)//parent存在且为红 进行调整{Node* grandfather = parent->_parent;//(1)parent在grandfater的左边//(2)parent在grandfater的右边if (parent == grandfather->_left)//parent在grandfater的左边{//情况1:uncle存在且为红,uncle和parent的颜色需要修改为黑,grandfater修改为红,如果满足循环条件继续往上更新//情况2:uncle存在且为黑,需要对红黑树进行旋转//情况3:uncle不存在,需要对红黑树进行旋转//注:情况2和情况3是一起处理的Node* uncle = grandfather->_right;if (uncle && uncle->_col == RED)//情况1{//修改颜色uncle->_col = parent->_col = BLACK;grandfather->_col = RED;//迭代往上更新cur = grandfather;parent = cur->_parent;}else//情况2 + 情况3{if (cur == parent->_left)//cur,parent,grandfater三个节点在一条直线上,单旋处理即可{RotateR(grandfather);//右单旋parent->_col = BLACK;grandfather->_col = RED;}else//cur,parent,grandfater三个节点是折线,需要双旋处理{RotateL(parent);//左单旋RotateR(grandfather);//右单旋cur->_col = BLACK;grandfather->_col = RED;}break;//旋转后,该子树的根变成了黑色,符合红黑树的特性,无需继续往上处理}}else//parent在grandfater的右边{//在右边 也是上面左边的三种情况Node* uncle = grandfather->_left;if (uncle && uncle->_col == RED)//情况1{//修改颜色uncle->_col = parent->_col = BLACK;grandfather->_col = RED;//迭代往上更新cur = grandfather;parent = cur->_parent;}else//情况2 + 情况3{if (cur == parent->_right)//cur,parent,grandfater三个节点在一条直线上,单旋处理即可{RotateL(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else//cur,parent,grandfater三个节点是折线,需要双旋处理{RotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;//旋转后,该子树的根变成了黑色,符合红黑树的特性,无需继续往上处理}}}_root->_col = BLACK;//根的颜色需要变为黑(原因是可能情况1会把根节点变红)return true;
}
注:红黑树其他接口就不实现了,在面试考的花也是考查红黑树的插入,即红黑树如何调平衡
五、红黑树的验证
红黑树的检测分为两步:
- 检测其是否满足二叉搜索树(中序遍历是否为有序序列)
- 检测其是否满足红黑树的性质
(1)中序检查
//中序遍历
void InOrder()
{_InOrder(_root);
}void _InOrder(Node* root)
{if (root == nullptr)return;_InOrder(root->_left);cout << root->_kv.first << ":" << root->_kv.second << endl;_InOrder(root->_right);
}
(2)检查红黑树特性
//检查红黑树特性
bool IsBalance()
{if (_root == nullptr){return true;}if (_root->_col != BLACK){cout << "违反规则:根节点不为黑色" << endl;return false;}Node* left = _root;int ref = 0;//用于一条路径上记录黑色节点的数量while (left)//求一条路径的黑色节点{if (left->_col == BLACK){++ref;}left = left->_left;}return Check(_root, 0, ref);
}//检查每条路径的黑色节点是否相等 && 是否出现连续红色节点
bool Check(Node* root, int blackNum, int ref)
{if (root == nullptr){if (blackNum != ref){cout << "违反规则:本条路径的黑色节点的数量跟最左路径不相等" << endl;return false;}return true;}if (root->_col == RED && root->_parent->_col == RED){cout << "违反规则:出现连续红色节点" << endl;return false;}if (root->_col == BLACK){++blackNum;}return Check(root->_left, blackNum, ref)&& Check(root->_right, blackNum, ref);
}
六、红黑树与AVL树的比较
红黑树和 AVL树都是高效的平衡二叉树,增删改查的时间复杂度都是O(logN),红黑树不追求绝对平衡,其只需保证最长路径不超过最短路径的2倍,相对而言,降低了插入和旋转的次数,所以在经常进行增删的结构中性能比 AVL树更优,而且红黑树实现比较简单,所以实际运用中红黑树更多
红黑树的应用:
- C++ STL库 -- map/set、mutil_map/mutil_set
- Java 库
- linux内核
- 其他一些库
七、完整代码
RBTree.h
#pragma onceenum Colour
{RED,BLACK,
};//K:key, V:value
template<class K, class V>
struct RBTreeNode
{//构造函数RBTreeNode(const pair<K, V>& kv):_kv(kv),_left(nullptr),_right(nullptr),_parent(nullptr),_col(RED){}//成员变量pair<K, V> _kv;RBTreeNode<K, V>* _left;RBTreeNode<K, V>* _right;RBTreeNode<K, V>* _parent;Colour _col;
};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* parent = nullptr;//用于记录上一个节点Node* cur = _root;//寻找合适的位置进行插入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//cur->kv.first == kv.first要插入值已经存在,插入失败{return false;}}cur = new Node(kv);cur->_col = RED;//新节点默认为红//插入if (parent->_kv.first < kv.first)//插入到parent左边{parent->_right = cur;cur->_parent = parent;}else//插入到parent右边{parent->_left = cur;cur->_parent = parent;}//进行调平衡 && 保持红黑树的特性,即插入节点的父亲是红色,需要对红黑树进行调整while (parent && parent->_col == RED)//parent存在且为红 进行调整{Node* grandfather = parent->_parent;//(1)parent在grandfater的左边//(2)parent在grandfater的右边if (parent == grandfather->_left)//parent在grandfater的左边{//情况1:uncle存在且为红,uncle和parent的颜色需要修改为黑,grandfater修改为红,如果满足循环条件继续往上更新//情况2:uncle存在且为黑,需要对红黑树进行旋转//情况3:uncle不存在,需要对红黑树进行旋转//注:情况2和情况3是一起处理的Node* uncle = grandfather->_right;if (uncle && uncle->_col == RED)//情况1{//修改颜色uncle->_col = parent->_col = BLACK;grandfather->_col = RED;//迭代往上更新cur = grandfather;parent = cur->_parent;}else//情况2 + 情况3{if (cur == parent->_left)//cur,parent,grandfater三个节点在一条直线上,单旋处理即可{RotateR(grandfather);//右单旋parent->_col = BLACK;grandfather->_col = RED;}else//cur,parent,grandfater三个节点是折线,需要双旋处理{RotateL(parent);//左单旋RotateR(grandfather);//右单旋cur->_col = BLACK;grandfather->_col = RED;}break;//旋转后,该子树的根变成了黑色,符合红黑树的特性,无需继续往上处理}}else//parent在grandfater的右边{//在右边 也是上面左边的三种情况Node* uncle = grandfather->_left;if (uncle && uncle->_col == RED)//情况1{//修改颜色uncle->_col = parent->_col = BLACK;grandfather->_col = RED;//迭代往上更新cur = grandfather;parent = cur->_parent;}else//情况2 + 情况3{if (cur == parent->_right)//cur,parent,grandfater三个节点在一条直线上,单旋处理即可{RotateL(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else//cur,parent,grandfater三个节点是折线,需要双旋处理{RotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;//旋转后,该子树的根变成了黑色,符合红黑树的特性,无需继续往上处理}}}_root->_col = BLACK;//根的颜色需要变为黑(原因是可能情况1会把根节点变红)return true;}//中序遍历void InOrder(){_InOrder(_root);}//检查红黑树特性bool IsBalance(){if (_root == nullptr){return true;}if (_root->_col != BLACK){cout << "违反规则:根节点不为黑色" << endl;return false;}Node* left = _root;int ref = 0;//用于一条路径上记录黑色节点的数量while (left)//求一条路径的黑色节点{if (left->_col == BLACK){++ref;}left = left->_left;}return Check(_root, 0, ref);}
private://检查每条路径的黑色节点是否相等 && 是否出现连续红色节点bool Check(Node* root, int blackNum, int ref){if (root == nullptr){if (blackNum != ref){cout << "违反规则:本条路径的黑色节点的数量跟最左路径不相等" << endl;return false;}return true;}if (root->_col == RED && root->_parent->_col == RED){cout << "违反规则:出现连续红色节点" << endl;return false;}if (root->_col == BLACK){++blackNum;}return Check(root->_left, blackNum, ref)&& Check(root->_right, blackNum, ref);}void _InOrder(Node* root){if (root == nullptr)return;_InOrder(root->_left);cout << root->_kv.first << ":" << root->_kv.second << endl;_InOrder(root->_right);}//左单旋void RotateL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;//进行链接parent->_right = subRL;if (subRL)subRL->_parent = parent;Node* ppNode = parent->_parent;//记录parent节点的前一个节点subR->_left = parent;parent->_parent = subR;if (ppNode == nullptr)//即subR已经是根节点{_root = subR;_root->_parent = nullptr;}else//subR不是根节点{//与上一个节点进行链接if (ppNode->_left == parent)//parent原本在 ppNode 的左边{ppNode->_left = subR;}else//parent原本在 ppNode 的右边{ppNode->_right = subR;}subR->_parent = ppNode;}}//右单旋void RotateR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;//进行链接parent->_left = subLR;if (subLR)subLR->_parent = parent;Node* ppNode = parent->_parent;//记录parent节点的前一个节点subL->_right = parent;parent->_parent = subL;if (ppNode == nullptr)//即subL已经是根节点{_root = subL;subL->_parent = nullptr;}else//subR不是根节点{//与上一个节点进行链接if (ppNode->_left == parent)//parent原本在 ppNode 的左边{ppNode->_left = subL;}else//parent原本在 ppNode 的右边{ppNode->_right = subL;}subL->_parent = ppNode;}}
private:Node* _root = nullptr;//缺省值
};
Test.cpp
#include <iostream>
using namespace std;
#include "RBTree.h"void TestRBTree1()
{//int arr[] = { 8, 3, 1, 10, 6, 4, 7, 14, 13 };//int arr[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };int arr[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };RBTree<int, int> t;for (auto e : arr){t.Insert(make_pair(e, e));}t.InOrder();
}void TestRBTree2()
{srand(time(0));//随机数种子const size_t N = 100000;RBTree<int, int> t;for (size_t i = 0; i < N; ++i){size_t x = rand();t.Insert(make_pair(x, x));//cout << t.IsBalance() << endl;}cout << t.IsBalance() << endl;
}int main()
{TestRBTree2();return 0;
}
----------------我是分割线---------------
文章到这里就结束了,下一篇即将更新
相关文章:

【C++进阶】四、红黑树(三)
目录 一、红黑树的概念 二、红黑树的性质 三、红黑树节点的定义 四、红黑树的插入 五、红黑树的验证 六、红黑树与AVL树的比较 七、完整代码 一、红黑树的概念 红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可…...

Spring——AOP切入点表达式和AOP通知类型
切入点:要进行增强的方法 切入点表达式:要进行增强的方法的描述式 第一种方法的本质是基于接口实现的动态代理(jdk) 第二种是基于cglib实现的动态代理 AOP切入点表达式 而需要加载多个切入点时,不可能每个切入点都写一个切入点表达式 例子 下面的代理描述的是匹配…...

Hadoop学习:Yarn
1.YARN介绍 一个通用的资源管理系统和调度平台 YARN不分配磁盘,由HDFS分配 相当于一个分布式的操作系统平台,为上层MR等计算程序提供运算所需要的资源(内存、CPU等) 2.YARN三大组件 不要忘记AppMaster,他是程序内部…...

Spring Data JPA
文章目录一、Spring Data基础概念二、JPA与JDBC的相同与不同之处三、Hibernate & JPA快速搭建1.添加依赖2.实体类3.hibernate的配置文件 ——hibernate.cfg.xml四、测试——基于hibernate的持久化(单独使用)五、测试——基于JPA的持久化(…...
java List报错Method threw ‘java.lang.UnsupportedOperationException‘ exception. 解决
问题描述:List使用Arrays.asList()初始化后,再add对象时报错:Method threw java.lang.UnsupportedOperationException exception.错误示例如下: List<ExportListVO.ExportSheet> sheetVOList Arrays.asList(new ExportList…...

数据结构-用栈实现队列
前言: 请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(push、pop、peek、empty): 实现 MyQueue 类: void push(int x) 将元素 x 推到队列的末尾 int pop() 从队列的开头移除并返回元素 int…...
第十四章 从 Windows 客户端控制 IRIS
文章目录第十四章 从 Windows 客户端控制 IRISIRISctlGetDirsSyntaxReturn ValuesIRISctlConfigStatusSyntaxReturn ValuesIRISctlControlSyntaxReturn Values第十四章 从 Windows 客户端控制 IRIS IRIS 为 Windows 客户端程序提供了一种机制来控制 IRIS 配置并启动 IRIS 进程…...

数据结构---双链表
专栏:数据结构 个人主页:HaiFan. 专栏简介:从零开始,数据结构!! 双链表前言双链表各接口的实现为要插入的值开辟一块空间BuyLN初始化LNInit和销毁LNDestory打印链表中的值LNPrint尾插LNPushBack和尾删LNPop…...

Windows 环境安装Scala详情
为了进一步学习Spark,必须先学习Scala 编程语言。首先开始Scala 环境搭建。温馨提示:本文是基于Windows 11 安装Scala 2.13.1 版本第一步:确保本机已经正确安装JDK1.8 环境第二步:Scala 官网下载我们所属scala版本文件。Scala 官网…...

C++ Qt自建网页浏览器
C Qt自建网页浏览器如需安装运行环境或远程调试,见文章底部个人QQ名片,由专业技术人员远程协助!前言这篇博客针对<<C Qt自建网页浏览器>>编写代码,代码整洁,规则,易读。 学习与应用推荐首选。文…...

Flink从入门到精通系列(四)
5、DataStream API(基础篇) Flink 有非常灵活的分层 API 设计,其中的核心层就是 DataStream/DataSet API。由于新版本已经实现了流批一体,DataSet API 将被弃用,官方推荐统一使用 DataStream API 处理流数据和批数据。…...

Nginx 配置实例-反向代理案例一
实现效果:使用nginx反向代理,访问 www.suke.com 直接跳转到本机地址127.0.0.1:8080 一、准备工作 Centos7 安装 Nginxhttps://liush.blog.csdn.net/article/details/125027693 1. 启动一个 tomcat Centos7安装JDK1.8https://liush.blog.csdn.net/arti…...

为什么北欧的顶级程序员数量远超中国?
说起北欧,很多人会想到寒冷的冬天,漫长的极夜,童话王国和圣诞老人,但是如果我罗列下诞生于北欧的计算机技术,恐怕你会惊掉下巴。Linux:世界上最流行的开源操作系统,最早的内核由Linus Torvalds开…...

vuex getters的作用和使用(求平均年龄),以及辅助函数mapGetters
getters作用:派生状态数据mapGetters作用:映射getters中的数据使用:方法名自定义,系统自动注入参数:state,每一个方法中必须有return,其return的结果被该方法名所接收。在state中声明数据listst…...

20230311给Ubuntu18.04下的GTX1080M安装驱动
20230311给Ubuntu18.04下的GTX1080M安装驱动 2023/3/11 12:50 2. 安装GTX1080驱动 安装 Nvidia 驱动 367.27 sudo add-apt-repository ppa:graphics-drivers/ppa 第一次运行出现如下的警告: Fresh drivers from upstream, currently shipping Nvidia. ## Curren…...
2023腾讯面试真题:
【腾讯】面试真题: 1、Kafka 是什么?主要应用场景有哪些? Kafka 是一个分布式流式处理平台。这到底是什么意思呢? 流平台具有三个关键功能: 消息队列:发布和订阅消息流,这个功能类似于消息…...
23种设计模式-建造者模式(Android应用场景介绍)
什么是建造者模式 建造者模式是一种创建型设计模式,它允许您使用相同的创建过程来生成不同类型和表示的对象。在本文中,我们将深入探讨建造者模式的Java实现,并通过一个例子来解释其工作原理。我们还将探讨如何在Android应用程序中使用建造者…...
English Learning - L2 语音作业打卡 双元音 [ʊə] [eə] Day17 2023.3.9 周四
English Learning - L2 语音作业打卡 双元音 [ʊə] [eə] Day17 2023.3.9 周四💌发音小贴士:💌当日目标音发音规则/技巧:🍭 Part 1【热身练习】🍭 Part2【练习内容】🍭【练习感受】🍓元音 [ʊə…...

【动态规划】多重背包问题,分组背包问题
Halo,这里是Ppeua。平时主要更新C语言,C,数据结构算法......感兴趣就关注我吧!你定不会失望。 🌈个人主页:主页链接 🌈算法专栏:专栏链接 我会一直往里填充内容哒! &…...

JAVA面向对象特征之——封装
4.封装 private关键字 是一个权限修饰符 可以修饰成员(成员变量和成员方法) 作用是保护成员不被别的类使用,被private修饰的成员只在本类中才能访问 针对private修饰的成员变量,如果需要被其他类使用,提供相应的操作 提供 “get变量名()…...
Vim 调用外部命令学习笔记
Vim 外部命令集成完全指南 文章目录 Vim 外部命令集成完全指南核心概念理解命令语法解析语法对比 常用外部命令详解文本排序与去重文本筛选与搜索高级 grep 搜索技巧文本替换与编辑字符处理高级文本处理编程语言处理其他实用命令 范围操作示例指定行范围处理复合命令示例 实用技…...

手游刚开服就被攻击怎么办?如何防御DDoS?
开服初期是手游最脆弱的阶段,极易成为DDoS攻击的目标。一旦遭遇攻击,可能导致服务器瘫痪、玩家流失,甚至造成巨大经济损失。本文为开发者提供一套简洁有效的应急与防御方案,帮助快速应对并构建长期防护体系。 一、遭遇攻击的紧急应…...
【根据当天日期输出明天的日期(需对闰年做判定)。】2022-5-15
缘由根据当天日期输出明天的日期(需对闰年做判定)。日期类型结构体如下: struct data{ int year; int month; int day;};-编程语言-CSDN问答 struct mdata{ int year; int month; int day; }mdata; int 天数(int year, int month) {switch (month){case 1: case 3:…...
React hook之useRef
React useRef 详解 useRef 是 React 提供的一个 Hook,用于在函数组件中创建可变的引用对象。它在 React 开发中有多种重要用途,下面我将全面详细地介绍它的特性和用法。 基本概念 1. 创建 ref const refContainer useRef(initialValue);initialValu…...

循环冗余码校验CRC码 算法步骤+详细实例计算
通信过程:(白话解释) 我们将原始待发送的消息称为 M M M,依据发送接收消息双方约定的生成多项式 G ( x ) G(x) G(x)(意思就是 G ( x ) G(x) G(x) 是已知的)࿰…...

关于nvm与node.js
1 安装nvm 安装过程中手动修改 nvm的安装路径, 以及修改 通过nvm安装node后正在使用的node的存放目录【这句话可能难以理解,但接着往下看你就了然了】 2 修改nvm中settings.txt文件配置 nvm安装成功后,通常在该文件中会出现以下配置&…...
【算法训练营Day07】字符串part1
文章目录 反转字符串反转字符串II替换数字 反转字符串 题目链接:344. 反转字符串 双指针法,两个指针的元素直接调转即可 class Solution {public void reverseString(char[] s) {int head 0;int end s.length - 1;while(head < end) {char temp …...

Aspose.PDF 限制绕过方案:Java 字节码技术实战分享(仅供学习)
Aspose.PDF 限制绕过方案:Java 字节码技术实战分享(仅供学习) 一、Aspose.PDF 简介二、说明(⚠️仅供学习与研究使用)三、技术流程总览四、准备工作1. 下载 Jar 包2. Maven 项目依赖配置 五、字节码修改实现代码&#…...

算法:模拟
1.替换所有的问号 1576. 替换所有的问号 - 力扣(LeetCode) 遍历字符串:通过外层循环逐一检查每个字符。遇到 ? 时处理: 内层循环遍历小写字母(a 到 z)。对每个字母检查是否满足: 与…...
A2A JS SDK 完整教程:快速入门指南
目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库ÿ…...