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

map和set(三)——红黑树

1、红黑树的概念及性质

1.1概念

概念:

红黑树是一种二叉搜索树,以颜色(Red or Black)互斥来限制每条路径不会另外的路径长出两倍,来达到近似平衡

1.2性质

红黑树的性质:

  1. 每个节点不是黑色就是红色
  2. 根节点是黑色的
  3. 如果一个节点是红色的,则它的两个孩子节点必须是黑色的(无连续的红节点)
  4. 对于每个节点,从该节点到其所有的后代叶节点的简单路径上,均包含相同数目的黑节点(每条路径都包含相同数量的黑色节点)
  5. 每个叶子节点都是黑色的(此叶子节点指的是NULL)

得知上面的有关红黑树的情况后,思考一个一个问题

它的性质如何保证最长路径不会超过最短路径的两倍?

考虑极端场景:

最短路径: 全黑                                最长路径:一直一黑一红

对比AVL树,高度是很接近logN

红黑树高度是很接近2*logN(红黑树搜索效率相对差一些,但几乎可以忽略不计)

但插入同样的数据,AVL树的高度更低,是通过多次旋转而得来的

2、红黑树的简单实现

2.1红黑树节点的定义

enum(枚举)里存的是节点的颜色

节点要有指向左节点、右节点、父节点的指针;节点存的值(数据)及节点的颜色

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

初始化(构造)节点,三指针指向空;_kv(值or数据)取决于传的值,_col默认为红色

为什么(插入)节点的颜色默认为红色呢?

如果插入黑色节点就会破坏规则:每条路径上黑色节点的数量相同

所以(插入)节点的默认颜色为红色

2.2插入

以下为简写:

u uncle 叔叔

p parent 父亲

g grandfather 爷爷

c cur遍历节点(可能是新插入节点)

情况一:

u(叔叔)存在且为红

g必为黑,若为红早就违反没有连续两个红色节点的规则

此时c节点一定是新插入的节点,且c节点的插入破坏了没有连续的红色节点的规则,所以我们需要对这颗红黑树进行调整

把g的颜色改为红色,将p/u的颜色改为黑色

注意:

g节点可能为根节点

若g节点为根节点,那么再将g节点变黑(根节点的颜色必须为黑色),所有路径的黑色节点+1,所有路径的黑色节点数依然相等

若g节点非根节点,那么只需要将g作为cur继续向上调整颜色

情况二 

叔叔不存在或叔叔存在且为黑

 若叔叔不存在

那么c节点一定是新插入节点,因为叔叔不存在,那么p父亲下面就不能再挂黑色节点了(往p下面挂p路径有黑色节点,而u却没有),不然就会违反"所有路径的黑色节点数量都相等"这条规则

若叔叔存在且为黑

那么c节点一定不是新插入的节点,若c节点为新插入节点,则在插入c之前就会违反"所有路径黑色节点数量相同"这一规则(g和u都为黑而p却非黑,c为插入默认为红,不平衡了);c不是新插入节点那么c节点下面一定有黑色节点对应黑色的u节点以达到平衡

 这时候我们就要用到之前AVL数所用的旋转了

叔叔不存在

叔叔存在且为黑

对g进行右旋(p为g的左、c为p的左),然后将p改为黑色,g改为红色

 上面的情况都是基于c节点在p节点左子树(左孩子节点)的条件下

当cur在p节点的右边,情况又是怎样的呢?

叔叔不存在

叔叔存在且为黑

当p为g的左而c为p的右边,单纯的单选已经解决不了了

要先对p进行左单旋,在对g进行右单旋(对比上下两个图就知道,其实只多了一步)

 

以上的所有情况都是基于父亲在爷爷左边的基础上的,还有父亲在爷爷右边的几种情况,不过和上面的大差不差,我就不细讲了 

红黑树其本质还是二叉搜索树,红黑树的插入还是以二叉搜索树的插入为基础所更改的

大概可以分为两步:

1. 以二叉搜索树的方式插入新节点

2. 检测新节点插入后,红黑树的性质是否造到破坏(对其进行旋转或变色)

 代码

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->_right;}else if (cur->_kv.first > kv.first){parent = cur;cur = cur->_left;}else{return false;}}//走到这说明已经找到可以插入的地方 //创建一个新节点cur = new Node(kv); // 红色的//判断插入的节点该连接到父节点的左还是右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 (parent == grandfather->_left)//父亲在爷爷左{Node* uncle = grandfather->_right;// 情况一:叔叔存在且为红if (uncle && uncle->_col == RED){// 变色parent->_col = uncle->_col = BLACK;grandfather->_col = RED;// 继续往上处理cur = grandfather;parent = cur->_parent;}else{// 情况二:叔叔不存在或者存在且为黑// 旋转+变色if (cur == parent->_left){RotateR(grandfather);						//       gparent->_col = BLACK;						//    p    ugrandfather->_col = RED;					// c}else{RotateL(parent);RotateR(grandfather);						//        gcur->_col = BLACK;							//    p       ugrandfather->_col = RED;					//      c}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 (cur == parent->_right){RotateL(grandfather);		//      gparent->_col = BLACK;		//   u     pgrandfather->_col = RED;	//            c}else{RotateR(parent);RotateL(grandfather);						//		 gcur->_col = BLACK;							//  u        pgrandfather->_col = RED;					//          c}break;}}}_root->_col = BLACK;return true;
}

左旋右旋的代码

void RotateL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;if (subRL)subRL->_parent = parent;subR->_left = parent;Node* ppnode = parent->_parent;parent->_parent = subR;if (parent == _root){_root = subR;subR->_parent = nullptr;}else{if (ppnode->_left == parent){ppnode->_left = subR;}else{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;subL->_right = parent;Node* ppnode = parent->_parent;parent->_parent = subL;if (parent == _root){_root = subL;subL->_parent = nullptr;}else{if (ppnode->_left == parent){ppnode->_left = subL;}else{ppnode->_right = subL;}subL->_parent = ppnode;}}

 

2.3红黑树的验证

红黑树的检测分为两步:

1. 检测其是否满足二叉搜索树(中序遍历是否为有序序列)

2. 检测其是否满足红黑树的性质

一般写验证的话要跟他的性质反着来,比如根节点的颜色是黑色的,那么如果根节点的颜色为红色就返回false

具体性质可以列为以下三条

根节点颜色为黑色

没有连续的红色节点

所有路径的黑色节点数量相同

根节点颜色为黑色已经讲过了

没有连续的红色节点可以在走中序的同时判断,(cur为遍历节点)若cur的颜色为红色且cur的parent的颜色也为红色那么返回false

至于所有路径的黑色节点数量相同

可以先统计一下最左路径黑色节点的数量作为参考值,然后如果有哪条路径的黑色节点数量不等于这个参考值的话就返回false

bool IsValidRBTRee()
{if (_root && _root->_col == RED){return false;}int refBlackNum = 0;//黑节点参考值Node* cur = _root;while (cur){if (cur->_col == BLACK){refBlackNum++;}cur = cur->_left;}return _IsValidRBTRee(_root, 0, refBlackNum);
}bool _IsValidRBTRee(Node* cur, size_t blackCount, size_t refBlack)
{if (cur == nullptr){if (refBlack != blackCount){cout << "黑色节点不相等" << endl;return false;}return true;}if (cur->_col == RED && cur->_parent->_col == RED){cout << "存在连续红色节点" << endl;return false;}if (cur->_col == BLACK)blackCount++;return _IsValidRBTRee(cur->_left, blackCount, refBlack)&& _IsValidRBTRee(cur->_right, blackCount, refBlack);
}

3.全部代码

#pragma once
enum Colour
{RED,BLACK
};template<class K, class V>
struct RBTreeNode
{RBTreeNode<K, V>* _left;RBTreeNode<K, V>* _right;RBTreeNode<K, V>* _parent;pair<K, V> _kv;Colour _col;RBTreeNode(const pair<K, V>& kv):_left(nullptr), _right(nullptr), _parent(nullptr), _kv(kv), _col(RED){}
};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->_right;}else if (cur->_kv.first > kv.first){parent = cur;cur = cur->_left;}else{return false;}}cur = new Node(kv); // 红色的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 (parent == grandfather->_left){Node* uncle = grandfather->_right;// 情况一:叔叔存在且为红if (uncle && uncle->_col == RED){// 变色parent->_col = uncle->_col = BLACK;grandfather->_col = RED;// 继续往上处理cur = grandfather;parent = cur->_parent;}else{// 情况二:叔叔不存在或者存在且为黑// 旋转+变色if (cur == parent->_left){RotateR(grandfather);						//       gparent->_col = BLACK;						//    p    ugrandfather->_col = RED;					// c}else{RotateL(parent);RotateR(grandfather);						//        gcur->_col = BLACK;							//    p       ugrandfather->_col = RED;					//      c}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 (cur == parent->_right){RotateL(grandfather);		//      gparent->_col = BLACK;		//   u     pgrandfather->_col = RED;	//            c}else{RotateR(parent);RotateL(grandfather);						//		 gcur->_col = BLACK;							//  u        pgrandfather->_col = RED;					//          c}break;}}}_root->_col = BLACK;return true;}/*获取红黑树最左侧节点*/Node* LeftMost(){Node* cur = _root;if (cur == nullptr ){return _root;}while (cur->_left){cur = cur->_left;}return cur;}// 获取红黑树最右侧节点Node* RightMost(){Node* cur = _root;if (nullptr == cur){return _root;}while (cur->_right){cur = cur->_right;}return cur;}// 检测红黑树是否为有效的红黑树,注意:其内部主要依靠_IsValidRBTRee函数检测bool IsValidRBTRee(){if (_root && _root->_col == RED){return false;}int refBlackNum = 0;//黑节点参考值Node* cur = _root;while (cur){if (cur->_col == BLACK){refBlackNum++;}cur = cur->_left;}return _IsValidRBTRee(_root, 0, refBlackNum);}void InOrder(){_InOrder(_root);cout << endl;}void _InOrder(Node* root){if (root == nullptr)return;_InOrder(root->_left);cout << root->_kv.first<<" ";_InOrder(root->_right);}void RotateL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;if (subRL)subRL->_parent = parent;subR->_left = parent;Node* ppnode = parent->_parent;parent->_parent = subR;if (parent == _root){_root = subR;subR->_parent = nullptr;}else{if (ppnode->_left == parent){ppnode->_left = subR;}else{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;subL->_right = parent;Node* ppnode = parent->_parent;parent->_parent = subL;if (parent == _root){_root = subL;subL->_parent = nullptr;}else{if (ppnode->_left == parent){ppnode->_left = subL;}else{ppnode->_right = subL;}subL->_parent = ppnode;}}bool _IsValidRBTRee(Node* cur, size_t blackCount, size_t refBlack){if (cur == nullptr){if (refBlack != blackCount){cout << "黑色节点不相等" << endl;return false;}return true;}if (cur->_col == RED && cur->_parent->_col == RED){cout << "存在连续红色节点" << endl;return false;}if (cur->_col == BLACK)blackCount++;return _IsValidRBTRee(cur->_left, blackCount, refBlack)&& _IsValidRBTRee(cur->_right, blackCount, refBlack);}
private:Node* _root = nullptr;
};void TestRBTree1()
{int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16,10,12};RBTree<int,int> t;for (auto e : a){t.Insert(make_pair(e, e));}t.InOrder();cout << t.IsValidRBTRee() << endl;
}

相关文章:

map和set(三)——红黑树

1、红黑树的概念及性质 1.1概念 概念&#xff1a; 红黑树是一种二叉搜索树&#xff0c;以颜色(Red or Black)互斥来限制每条路径不会比另外的路径长出两倍&#xff0c;来达到近似平衡 1.2性质 红黑树的性质&#xff1a; 每个节点不是黑色就是红色根节点是黑色的如果一个节点是…...

Day26 HashMap

Day26 HashMap 文章目录 Day26 HashMap一、应用场景二、特点三、基本用法四、面试题 一、应用场景 1、概念&#xff1a; HashMap是Java集合框架中的一种实现类&#xff0c;用于存储键值对。 2、好处&#xff1a; HashMap是一个常用的集合类&#xff0c;适用于需要快速查找和插…...

某蓝队面试经验

背景 据小道消息说今年的国护疑似提前到了五月份&#xff0c;所以最近也是HW面试的一个高峰期啊&#xff0c;这里分享一下上次长亭的蓝队面试问题&#xff08;附本人的回答&#xff0c;仅供参考&#xff09; 面试问答 1、谈谈作为蓝队护网过程使用过厂商的设备 这里我回答的…...

【Linux】 centos7安装卸载SQL server(2017、2019)

一、安装配置 准备一个基础Linux配置&#xff1a; 内存为20GB 运行内存为2GB的系统&#xff08;数据库小于2GB安装不了&#xff09; 1、网络配置 我们需要进行网络的连接 进入 cd /ect/sysconfig/network-script/ 编辑文件ifcfg-ens33 vi ifcfg-ens33 Insert键进行编辑 把ONBOO…...

面试算法-110-课程表

题目 你这个学期必须选修 numCourses 门课程&#xff0c;记为 0 到 numCourses - 1 。 在选修某些课程之前需要一些先修课程。 先修课程按数组 prerequisites 给出&#xff0c;其中 prerequisites[i] [ai, bi] &#xff0c;表示如果要学习课程 ai 则 必须 先学习课程 bi 。 …...

注册前后端php的检测

首先&#xff0c;在HTML表单中添加一个用于输入密码的文本框&#xff0c;并在其后面添加一个用于显示密码格式要求提示的元素&#xff0c;例如一个 <span> 标签。 <input type"password" id"passwordInput"> <span id"passwordHint…...

Redis:什么是redis?①

一、思想 Redis是一个开源的高性能基于内存key-value数据库&#xff0c;常用作数据库、缓存或消息代理 二、数据类型 String List...

【课程】MyBatisPlus视频教程

MyBatis-Plus是一款非常强大的MyBatis增强工具包,只做增强不做改变. 在不用编写任何SQL语句的情况下即可以极其方便的实现单一、批量、分页等操作。 本套教程基于MyBatis-Plus新2.3版本,详细讲授&#xff1a;集成Mybatis-Plus、 通用CRUD、EntityWrapper条件构造器、ActiveRec…...

如何使用人工智能和ChatGPT来优化营销转化率

人工智能 &#xff08;AI&#xff09; 和营销的交集正在彻底改变企业与客户互动的方式&#xff0c;最终改变营销转化率。人工智能能够分析大量数据、理解模式和自动执行任务&#xff0c;它不仅是一项创新技术&#xff0c;而且是营销领域的根本性转变。这种转变允许更加个性化、…...

Ubuntu 22.04上构建libvirt源码错误解决

当在Ubuntu 22.04上构建libvirt源码时&#xff0c;可能会遇到一些错误。下面是一些常见错误及其解决方法&#xff1a; 1. 错误&#xff1a;Program xmllint’未找到或不可执行 解决方法&#xff1a;安装libxml2-utils sudo apt-get install libxml2-utils2. 错误&#xff1a…...

游戏客户端面经

1&#xff0c;3D的模型怎么显示到2DUI上面 2&#xff0c;C#的ArryList和List的区别 3&#xff0c;接口和抽象类的区别&#xff0c;一般什么时候用接口 4&#xff0c;UGUI怎么渲染的UI&#xff0c;UGUI的层级管理&#xff08;怎么不打断合批&#xff09;&#xff0c;合批流程…...

AS,idea,maven,gradle

Jdk,sdk。提前都是需要下好的。 Maven与gradle的思考&#xff1a; 用AS开发app时&#xff0c;gradle本就有&#xff0c;自己也可以指定&#xff0c;AGP同样。要注意gradle&#xff0c;AGP,jdk版本的事情。还有依赖库。 用idea开发网络程序时&#xff0c;也有内置的maven&…...

ElasTool v3.0 程序:材料弹性和机械性能的高效计算和可视化工具包

分享一个材料弹性和机械性能的高效计算和可视化工具包&#xff1a; ElasTool v3.0。 感谢论文的原作者&#xff01; 主要内容 “弹性和机械性能的高效计算和可视化对于材料的选择和新材料的设计至关重要。该工具包标志着材料弹性和机械性能计算分析和可视化方面的重大进步…...

Redis入门级详解(一)

一、Redis入门介绍 1、什么是Redis? Redis&#xff0c;英文全称是Remote Dictionary Server&#xff08;远程字典服务&#xff09;&#xff0c;是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库&#xff0c;并提供多种语言的API。…...

java算法题每日多道六

138. 随机链表的复制 题目 给你一个长度为 n 的链表&#xff0c;每个节点包含一个额外增加的随机指针 random &#xff0c;该指针可以指向链表中的任何节点或空节点。 构造这个链表的 深拷贝。 深拷贝应该正好由 n 个 全新 节点组成&#xff0c;其中每个新节点的值都设为其对…...

C# 特性(Attribute)

C# 特性&#xff08;Attribute&#xff09; 文章目录 C# 特性&#xff08;Attribute&#xff09;Obsolete语法示例代码 创建自定义特性&#xff08;Attribute&#xff09; Obsolete 这个预定义特性标记了不应被使用的程序实体。它可以让您通知编译器丢弃某个特定的目标元素。例…...

Redis 教程系列之Redis 配置(三)

Redis 配置 Redis 的配置文件位于 Redis 安装目录下,文件名为 redis.conf(Windows 名为 redis.windows.conf)。 你可以通过 CONFIG 命令查看或设置配置项。 语法 Redis CONFIG 命令格式如下: redis 127.0.0.1:6379> CONFIG GET CONFIG_SETTING_NAME 实例 redis 127.0…...

Java实验03

Code1 package q3;public class Method01{public static void main(String[] args) {class Student{String name;String StuID;public Student(String name,String StuID){this.namename;this.StuIDStuID;}public void speak(String name, String stuID) {//输出学号与姓名Sys…...

安卓studio连接手机之后,一两秒之后就自动断开了。问题解决。

太坑了&#xff0c;安卓studio链接手机之后。几秒之后就断开了。我以为是adb的问题&#xff0c;就重新安装了一下adb。并且在环境变量中配置了Path的路径。然而并没有什么用啊。 经过排查原来是数据心虚了。线的接触不良。导致你刚接通的瞬间有相对较强的电流是因为有瞬间高电压…...

数字科技优化金融供给,内外协同激活新质生产力

来源 | 镭射财经&#xff08;leishecaijing&#xff09; 新一轮产业变革悄然发生&#xff0c;决定产业高度和竞争格局的底层生产力&#xff0c;也正在经历一场从量变到质变的跃迁。新质生产力则是这场跃迁后的最新呈现。 站在新质生产力爆发的时代拐点&#xff0c;金融业达成…...

RC4算法逆向实战:从特征识别到魔改对抗

1. RC4算法基础与逆向特征识别 RC4算法作为经典的流加密算法&#xff0c;在CTF竞赛和恶意软件分析中频繁出现。我第一次逆向分析RC4加密的样本时&#xff0c;花了整整三天才确认算法类型——因为当时的我还不熟悉它的特征指纹。现在回头看&#xff0c;识别标准RC4其实有明确的规…...

药流会不会落下月子病?药流后修护要点

药流作为终止早期妊娠的常见方式&#xff0c;其术后养护是否到位&#xff0c;直接关系到女性后续健康&#xff0c;“药流会不会落下月子病”也是行业内及女性群体重点关注的问题。事实上&#xff0c;药流虽无需手术创伤&#xff0c;但对身体的隐性损伤不容忽视&#xff0c;若忽…...

Python数据分析项目实战(046)——数据清洗与预处理概述

版权声明 本文原创作者:谷哥的小弟 作者博客地址:http://blog.csdn.net/lfdfhl 数据清洗与预处理是数据分析的基础环节。本阶段工作旨在修复数据质量问题、规范数据格式、优化数据结构,为后续分析建模提供可靠数据。 缺失值处理 缺失值指数据集中存在的空值或未记录的信息…...

2025届最火的五大降AI率平台横评

Ai论文网站排名&#xff08;开题报告、文献综述、降aigc率、降重综合对比&#xff09; TOP1. 千笔AI TOP2. aipasspaper TOP3. 清北论文 TOP4. 豆包 TOP5. kimi TOP6. deepseek 一款基于自然语言处理技术的智能工具&#xff0c;是AI写作软件&#xff0c;它能够辅助用户自…...

Muon最佳实践:10个提升开发效率的实用技巧

Muon最佳实践&#xff1a;10个提升开发效率的实用技巧 【免费下载链接】muon GPU based Electron on a diet 项目地址: https://gitcode.com/gh_mirrors/mu/muon Muon作为一款基于GPU的轻量级Electron替代方案&#xff0c;采用Golang开发并使用Ultralight引擎&#xff0…...

FALCON: Fast Autonomous Aerial ExplorationUsing Coverage Path Guidance(覆盖路径引导的快速自主空中探索)

创新点&#xff1a;提出一种基于连接性的增量式空间分解和连接图构造方法&#xff0c;捕获环境拓扑并促进有效的探测覆盖路径规划提出一种分层的探索规划方法&#xff0c;生成合理的覆盖路径作为全局指导&#xff0c;并优化局部边界访问顺序&#xff0c;保持覆盖路径的意图。提…...

MongoDB Compass完全指南:从安装到精通的5个关键步骤

MongoDB Compass完全指南&#xff1a;从安装到精通的5个关键步骤 【免费下载链接】compass The GUI for MongoDB. 项目地址: https://gitcode.com/gh_mirrors/com/compass 数据库管理是否还在依赖命令行输入&#xff1f;面对复杂的JSON结构是否感到无从下手&#xff1f;…...

2种开源工具解决方案解决Beyond Compare 5授权失效问题

2种开源工具解决方案解决Beyond Compare 5授权失效问题 【免费下载链接】BCompare_Keygen Keygen for BCompare 5 项目地址: https://gitcode.com/gh_mirrors/bc/BCompare_Keygen Beyond Compare 5作为一款专业的文件比较与同步工具&#xff0c;在软件开发和数据管理领域…...

飞书推送文件给指定用户

首先要先把文件上传到飞书服务器&#xff0c;获取文件key。然后调用消息发送API进行文件推送// 上传文件String fileKey uploadFileToFeishu();// 将文件推送给用户列表sendFileToFeishuUserId(fileKey,userList);/*** 上传文件到飞书云端* return* throws Exception*/privat…...

证书创建方法说明

生成证书 方法一&#xff1a;合适&#xff08;临时测试&#xff0c;不需要管理&#xff09; 快速生成脚本&#xff08;一键完成&#xff09; 创建 create_lan_cert.sh&#xff1a; #!/bin/bash# 配置参数 IP_ADDR"192.168.1.100" # 修改为你的局域网IP DAYS365…...