数据结构初阶——红黑树的实现(C++) - 教程
目录
平衡搜索二叉树
红黑树的相关概念
红黑树的性质
红黑树节点的定义
红黑树的插入
红黑树的验证
红黑树的查找
红黑树的删除
红黑树对比AVL树
红黑树的整体实现
平衡搜索二叉树
我们常见的平衡搜索二叉树有AVL树、红黑树和B树(及其变种B+树),我们这里所讨论的就是红黑树了,这个结构比较的抽象,但是我们要明白一点:我们的平衡搜索二叉树的本质就是平衡,也就是通过一种自平衡的机制使得树可以不退化为链表。友情提醒:红黑树的实现还是很复杂的,一般的公司笔面对这个的要求就是了解即可。
红黑树的相关概念
我们这里的红黑树就是用到了红色和黑色的节点的树(字面意思),我们用的是两种区分的节点的数量根据一套规则来控制树的平衡性的,这里的区分也可以是别的东西,我们这里的平衡不像AVL树那么的严格,而是保证没有一条从根节点到叶节点的路径比其他任意路径长出两倍,这就是一种近似的平衡效果,如图:
红黑树的性质
我们想实现红黑树就必须要了解它的相关性质,我们总结了红黑的4点性质:
1、除了根节点是黑色的,每个节点不是红色就是黑色。
2、一个节点是红色,那么它的两个孩子就是黑色的。
3、对于每一个节点,从这个节点到其所有后代的叶子节点的路径上包含相同数目的黑色节点。
4、每一个叶子节点都是黑色的。
我们怎么理解从根节点到叶子节点的最长路径不会超过最短路径的两倍呢?
我们想要验证这个结论就需要指出我们根据上面的性质所能实现的最长和最短路径了:
我们首先假设我们所有路径上包含的黑色节点数量是N个,最短路径就是将这N个节点连起来, 长度是N:
我们的最长路径就是一个黑色一个红色,让红色节点尽量的多,我们这个路径的黑色节点和红色节点的数目相同长度是2N:
我们这里算长度没有算叶节点,这样我们就说明我们是可以保证我们的近似平衡条件的。
红黑树节点的定义
我们这里的红黑树的节点定义为三分叉的结构,然后再加入颜色这一成员:
enum Colour {
RED,
BLACK
};
template
struct RBTreeNode {
RBTreeNode* _left;
RBTreeNode* _right;
RBTreeNode* _parent;
pair _kv;
int _col;
RBTreeNode(const pair& kv)
:_left(nullptr),
_right(nullptr),
_parent(nullptr),
_kv(kv),
_col(RED) {}
};
我们这里默认将节点的颜色设置成了红色的,我们这里可以分析一下设置成黑色和红色会出现的情况:
- 如果我们将这个节点默认设置成了黑色的,那么我们一上来就破坏了性质3的条件,这个时候我们就需要对树进行调整了。
- 如果我们将这个节点设置成了红色的,我们就会有两种情况了,第一种情况就是这个节点的父节点是红色的,那也就表明破坏了性质2的条件,但是如果该节点的父节点是黑色的就不会有什么差错。
所以综上所述我们优先考虑将节点默认设置成了红色。
红黑树的插入
我们插入节点分成下面三个步骤:
1、我们根据二叉搜索树的插入方式找到我们要插入的位置。
2、将插入的节点插入到树中。
3、如果插入的父节点是红色的,就要对红黑树进行调整了。
我们这里最重要的还是最后一个步骤,因为这里会有很多的情况发生。
我们插入节点后应该怎么调整呢?
我们这里插入节点主要是针对插入节点后的父节点为红色进行判断的,因为黑色不需要调整。
我们这里先说明一个情况的必要,那就是我们的祖父节点(父亲的父亲)一定是存在的,因为根节点不可能是红色(性质1)。
我们这里的调整主要是看插入节点的叔叔节点(插入节点的父节点的兄弟节点),根据插入节点的叔叔的不同,我们将红黑树的调整分成了下面三种情况:
第一种情况:插入节点有叔叔节点,且叔叔节点是红色的。
这个时候为了使得不出现连续的红色节点,我们将父节点变成黑色,但是为了保持黑色节点的数目不变,我们将祖父节点变成红色,然后将叔叔节点也变成黑色:
但是我们还没有结束,因为我们的祖父节点有可能是根节点,这个时候我们就要将祖父节点变成黑色,这个时候就相当于每一条路径上都增加了一个黑色的节点。
但是如果祖父节点不是根节点,我们就需要将祖父节点作为新插入的节点,再次重复上面的操作了。
第二种情况:插入节点的叔叔节点存在,且叔叔节点是黑色。
这种情况的cur一定不是新插入的节点,而是第一种情况中的向上调整的过程的祖父节点的重新插入如图:
我们这里讲插入之前的祖父节点上面黑色节点的数量设置成x,将叔叔节点下面黑色节点的数量设置为y,那么就会出现图示的两条路经的节点数量为x + 1 和 x + y + 2的情况,我们很明显地发现x + 2 + y是要大于x + 1的,这样就不会满足红黑树的要求,再因为我们的情况2和3都只是循环处理,不会向上调整,所以我们的这一情况只能是情况1来的。
这种情况下,我们单纯的使用变色是不能实现我们要的效果的,我们需要像实现AVL一样使用旋转的方式来进行处理,如果我们的祖孙三代是直线关系也就是(cur、parent和grandfather这三个节点在同一条直线上面),这个时候我们就要进行单旋的操作了,然后才是调整颜色。
这里还是要说明一下,我们这里的直线关系就是p是g的右孩子,cur是parent的右孩子时需要进行左单旋的操作,再进行颜色的调整。
如果祖孙三代的关系是折线的关系,我们就要进行双旋的操作了,然后再进行颜色的调整。
第三种情况:插入节点的叔叔节点不存在
这样的情况一定是新插入的节点,不可能是情况以来的,因为叔叔节点都不存在了说明节点的数量就是祖父节点往上的数量,不可能是包括了下面的。
和第二种情况一样我们也是分成了两种线性的关系来讨论的,如果是祖孙三代的关系是直线的话,我们就要进行单旋然后再是进行颜色的调整,这样操作之后我们的根节点就是黑色的了,就不需要进行向上的处理了。
如果是折线的关系,和上面的情况2是一样的进行双旋之后,再进行颜色的调整,调整完了之后就没有其他的操作了。
实现代码如下:
bool insert(const pair& 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 _right;
}else if(cur->_kv.first > kv.first) {
parent = cur;
cur = cur->_left;
}else {
return false;
}
}
cur = new Node(kv);
if(parent->_kv.first _right = cur;
}else {
parent->_left = cur;
}
cur->_parent = parent;
// 我们要处理的是父亲是红色的
while(parent && parent->_col == RED) {
Node* grandfather = parent->_parent;
if(parent == grandfather->_left) {
// 这个情况
// g
//p u
Node* uncle = grandfather->_right;
// 有uncle且为红色,变颜色继续处理
if(uncle && uncle->_col == RED) {
parent->_col = uncle->_col = BLACK;
grandfather->_col = RED;
// 继续处理上面
cur = grandfather;
parent = cur->_parent;
}else { // uncle为黑色,旋转变颜色
if(cur == parent->_left) {
// g
// p u
//c
RotateR(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}else {
// g
// p u
// c
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 { // 不存在叔叔,或者是为黑色
// g
// u p
// c
if(cur == parent->_right) {
RotateL(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}else {
RotateR(parent);
RotateL(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
break;
}
}
}
_root->_col = BLACK; // 这里可能被情况1变成了红色
return true;
}
void RotateR(Node* parent) {
Node* subL = parent->_left;
Node* subLR = subL->_right;
parent->_left = subLR;
if(subLR) {
subLR->_parent = parent;
}
Node* pParent = parent->_parent;
subL->_right = parent;
parent->_parent = subL;
if(parent == _root) {
_root = subL;
subL->_parent = nullptr;
}else {
if(pParent->_left == parent) {
pParent->_left = subL;
}else {
pParent->_right = subL;
}
subL->_parent = pParent;
}
}
void RotateL(Node* parent) {
Node* subR = parent->_right;
Node* subRL = subR->_left;
if(subRL) {
subRL->_parent = parent;
}
Node* pParent = parent->_parent;
subR->_left = parent;
parent->_parent = subR;
if(pParent == nullptr) {
_root = subR;
subR->_parent = nullptr;
}else {
if(parent == pParent->_left) {
pParent->_left = subR;
}else {
pParent->_right = subR;
}
subR->_parent = pParent;
}
}
敲黑板:
这里重点要注意的是我们的根节点很有可能被设置成了红色的,所以我们这里统一的最后强制置为黑色的。
红黑树的验证
我们实现完了红黑树之后,如何判断是否合格呢?首先我们要判断它是不是满足二叉搜索树的,其次再判断是不是满足我们上面写到的几点性质。
第一步:满足二叉搜索树
代码如下:
void InOrder() {
_InOrder(_root);
cout _left);
cout _kv.first _kv.second _right);
}
第二步:是不是满足我们的性质
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);
}
bool Check(Node* root, int num, const int refNUm) {
if(root == nullptr) {
if(num != refNUm) {
cout _col == RED && root->_parent->_col == RED) {
cout _kv.first _col == BLACK) {
num++;
}
return Check(root->_left, num, refNUm) && Check(root->_right, num, refNUm);
}
红黑树的查找
红黑树的查找的实现和我们的二叉搜索树的查找几乎没什么两样,逻辑如下:
1、如果是空树,那么查找失败返回nullptr。
2、如果是key值小于当前节点的值,就到该节点的左子树中找。
3、如果是key值大于当前节点的值,就到该节点的右子树中找。
4、如果是key值等于当前节点的值,查找就成功了,就返回对应的节点。
代码如下:
Node* Find(const K& key) {
Node* cur = _root;
while(cur) {
if(cur->_kv.first _left;
}else if(cur->_kv.first > key) {
cur = cur->_right;
}else {
return cur;
}
}
return nullptr;
}
红黑树的删除
这里就不实现了,因为这个操作有点困难并且考到的概率极低,有兴趣的友友可以去小破站上找找视频。
红黑树对比AVL树
我们的红黑树和AVL树都是比较高效的平衡二叉树,增删改查的时间复杂度都是O(logN),但是这两个树控制控制平衡的方式很不一样:
- AVL树是通过控制左右高度差不超过1来实现的。
- 红黑树是通过控制不同颜色的节点,来使得最长路径不超过最短路径的2倍的,其实是一种近似的平衡。
由于我们的红黑树实现的是近似平衡,所以红黑树降低了插入节点时需要进行的旋转的次数,所以在经常进行增删的结构中红黑树的性能更好,实际的运用中也是红黑树居多。
红黑树的整体实现
#include
enum Colour
{
RED,
BLACK
};
template
struct RBTreeNode
{
// 这里更新控制平衡也要加入parent指针
T _data;
RBTreeNode* _left;
RBTreeNode* _right;
RBTreeNode* _parent;
Colour _col;
RBTreeNode(const T& data)
:_data(data)
, _left(nullptr)
, _right(nullptr)
, _parent(nullptr)
{}
};
template
struct RBTreeIterator
{
typedef RBTreeNode Node;
typedef RBTreeIterator Self;
Node* _node;
Node* _root;
RBTreeIterator(Node* node, Node* root)
:_node(node)
,_root(root)
{}
Self operator++()
{
if (_node->_right)
{
// 右不为空,中序下一个访问的节点是右子树的最左(最小)节点
Node* min = _node->_right;
while (min->_left)
{
min = min->_left;
}
_node = min;
}
else
{
// 右为空,祖先里面孩子是父亲左的那个祖先
Node* cur = _node;
Node* parent = cur->_parent;
while (parent && cur == parent->_right)
{
cur = parent;
parent = cur->_parent;
}
_node = parent;
}
return *this;
}
Self operator--()
{
if (_node == nullptr) // --end()
{
// --end(),特殊处理,走到中序最后一个结点,整棵树的最右结点
Node* rightMost = _root;
while (rightMost && rightMost->_right)
{
rightMost = rightMost->_right;
}
_node = rightMost;
}
else if (_node->_left)
{
// 左子树不为空,中序左子树最后一个
Node* rightMost = _node->_left;
while (rightMost->_right)
{
rightMost = rightMost->_right;
}
_node = rightMost;
}
else
{
// 孩子是父亲右的那个祖先
Node* cur = _node;
Node* parent = cur->_parent;
while (parent && cur == parent->_left)
{
cur = parent;
parent = cur->_parent;
}
_node = parent;
}
return *this;
}
Ref operator*()
{
return _node->_data;
}
Ptr operator->()
{
return &_node->_data;
}
bool operator!= (const Self& s) const
{
return _node != s._node;
}
bool operator== (const Self& s) const
{
return _node == s._node;
}
};
template
class RBTree
{
typedef RBTreeNode Node;
public:
typedef RBTreeIterator Iterator;
typedef RBTreeIterator ConstIterator;
Iterator Begin()
{
Node* cur = _root;
while (cur && cur->_left)
{
cur = cur->_left;
}
return Iterator(cur, _root);
}
Iterator End()
{
return Iterator(nullptr, _root);
}
ConstIterator Begin() const
{
Node* cur = _root;
while (cur && cur->_left)
{
cur = cur->_left;
}
return ConstIterator(cur, _root);
}
ConstIterator End() const
{
return ConstIterator(nullptr, _root);
}
pair Insert(const T& data)
{
if (_root == nullptr)
{
_root = new Node(data);
_root->_col = BLACK;
//return pair(Iterator(_root, _root), true);
return { Iterator(_root, _root), true };
}
KeyOfT kot;
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (kot(cur->_data) _right;
}
else if (kot(cur->_data) > kot(data))
{
parent = cur;
cur = cur->_left;
}
else
{
return { Iterator(cur, _root), false };
}
}
cur = new Node(data);
Node* newnode = cur;
cur->_col = RED;
if (kot(parent->_data) _right = cur;
}
else
{
parent->_left = cur;
}
// 链接父亲
cur->_parent = parent;
// 父亲是红色,出现连续的红色节点,需要处理
while (parent && parent->_col == RED)
{
Node* grandfather = parent->_parent;
if (parent == grandfather->_left)
{
// g
// p u
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)
{
// g
// p u
// c
RotateR(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
else
{
// g
// p u
// c
RotateL(parent);
RotateR(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
break;
}
}
else
{
// g
// u p
Node* uncle = grandfather->_left;
// 叔叔存在且为红,-》变色即可
if (uncle && uncle->_col == RED)
{
parent->_col = uncle->_col = BLACK;
grandfather->_col = RED;
// 继续往上处理
cur = grandfather;
parent = cur->_parent;
}
else // 叔叔不存在,或者存在且为黑
{
// 情况二:叔叔不存在或者存在且为黑
// 旋转+变色
// g
// u p
// c
if (cur == parent->_right)
{
RotateL(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
else
{
RotateR(parent);
RotateL(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
break;
}
}
}
_root->_col = BLACK;
return { Iterator(newnode, _root), true };
}
void RotateR(Node * parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
parent->_left = subLR;
if (subLR)
subLR->_parent = parent;
Node* pParent = parent->_parent;
subL->_right = parent;
parent->_parent = subL;
if (parent == _root)
{
_root = subL;
subL->_parent = nullptr;
}
else
{
if (pParent->_left == parent)
{
pParent->_left = subL;
}
else
{
pParent->_right = subL;
}
subL->_parent = pParent;
}
}
void RotateL(Node * parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
parent->_right = subRL;
if (subRL)
subRL->_parent = parent;
Node* parentParent = parent->_parent;
subR->_left = parent;
parent->_parent = subR;
if (parentParent == nullptr)
{
_root = subR;
subR->_parent = nullptr;
}
else
{
if (parent == parentParent->_left)
{
parentParent->_left = subR;
}
else
{
parentParent->_right = subR;
}
subR->_parent = parentParent;
}
}
Node* Find(const K& key)
{
Node* cur = _root;
while (cur)
{
if (cur->_kv.first _right;
}
else if (cur->_kv.first > key)
{
cur = cur->_left;
}
else
{
return cur;
}
}
return nullptr;
}
int Height()
{
return _Height(_root);
}
int Size()
{
return _Size(_root);
}
private:
int _Height(Node* root)
{
if (root == nullptr)
return 0;
int leftHeight = _Height(root->_left);
int rightHeight = _Height(root->_right);
return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
}
int _Size(Node* root)
{
if (root == nullptr)
return 0;
return _Size(root->_left) + _Size(root->_right) + 1;
}
private:
Node* _root = nullptr;
};
相关文章:
数据结构初阶——红黑树的实现(C++) - 教程
数据结构初阶——红黑树的实现(C++) - 教程pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", monospace !im…...
CMC蒲和平3.1
例3(凑)求 \(\int\frac{dx}{\sqrt[3]{(x + 1) ^ 2(x - 1) ^ 4}}\).solution 注意到 \(d(\frac{x + 1}{x - 1}) = \frac{-2}{(x - 1) ^ 2} dx\),考虑凑微分。 \[I = \int \frac{dx}{\sqrt[3]{(\frac{x + 1}{x - 1}) ^ 2} (x - 1) ^ 2} = -\frac{1}{2}\int \frac{d(\frac{x + …...
解码C语言数组
一维数组 数组是相同类型数据元素的有序集合,通过下标(索引)访问元素,内存中连续存储。 数组名表示首元素地址,sizeof(arr) 返回整个数组的字节大小 核心特点元素类型一致:所有元素必须为同一数据类型(如 int, float)。 固定大小:数组长度在声明时确定,静态数组无法动…...
github启用Disscussions讨论功能
配置步骤 1. 设置GitHub仓库并启用Discussions功能 首先需要为你的GitHub仓库启用Discussions功能:访问你的GitHub仓库: https://github.com/KkaiFang/my_notes点击 Settings 标签页向下滚动找到 Features 部分勾选 Discussions 复选框来启用讨论功能2. 配置Giscus评论系统 访…...
RWA技术规范解读:如何实现现实世界资产的合规代币化
RWA技术规范解读:如何实现现实世界资产的合规代币化 近日,深圳市信息服务业区块链协会发布了《RWA技术规范》(T/SZBA-2025),这是国内首个针对现实世界资产代币化的团体标准。本文将深入解读该规范的核心内容,帮助读者全面了解RWA代币化的技术框架和实施要点。 1. 什么是RWA…...
干货预警!Apache SeaTunnel 助力多点 DMALL 构建数据集成平台,探索AI新零售行业应用!
🎉亲爱的社区朋友们,数据集成领域的一场知识盛宴即将来袭!9 月 30 日下午 2 点,Apache SeaTunnel 社区精心策划的又一场线上 Meetup 将准时与大家云端相见!🎉亲爱的社区朋友们,数据集成领域的一场知识盛宴即将来袭!9 月 30 日下午 2 点,Apache SeaTunnel 社区精心策…...
Apache SeaTunnel 2.3.12 发布!核心引擎升级、连接器生态再扩张
近期,Apache SeaTunnel 2.3.12 正式发版。这是继 2.3.11 之后的又一次迭代,本周期合并 82 个 PR,提供 9 项新特性、30+ 项功能增强、20+ 处文档修正,并修复 43 个 Bug。核心改进集中在 SensorsData 与 Databend 生态接入,Paimon、ClickHouse、MaxCompute 等连接器读写能力…...
详细介绍:对于牛客网—语言学习篇—C语言入门—链表的题目解析
详细介绍:对于牛客网—语言学习篇—C语言入门—链表的题目解析pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New&quo…...
Day17Arrays类的初步认识
package com.cc.array;import java.util.Arrays;public class ArrayDem6 {public static void main(String[] args) {int[] a = {12, 3, 43, 4, 235, 5, 6, 45, 7, 7};System.out.println(a);//[I@f6f4d33//打印数组元素System.out.println(Arrays.toString(a));//toString:以字…...
小学生模拟赛题解
A 正常做这题显然 \(10^{18}\) 是不可做的,所以问题一定出现在 gen 上。 注意到 \(7\mid2009\),换句话说,若 \(t_1=3k(k\in\mathbb N_+)\),那么 \(t_2=t_1+9\),这就导致 \(3\mid t_2\)。以此类推,会发现对于 \(\forall i\in[2,n]\),满足 \(t_i-t_{i-1}=9\),答案就是 \(…...
服务器安装docker、mysql、redis、nginx、nacos、jdk等
一、安装docker 1.1、安装必要工具 sudo yum install -y yum-utils \ device-mapper-persistent-data \ lvm21.2、进行仓库源设置 sudo yum-config-manager \ --add-repo \ https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/centos/docker-ce.repo1.3、docker安装安装最新…...
StringComparer.OrdinalIgnoreCase
StringComparer.OrdinalIgnoreCase 是 .NET 提供的不区分大小写、且按 Unicode 码位排序的字符串比较器,适用于哈希表、字典、集合、排序等需要显式指定比较规则的地方。1. 核心特点特性说明比较规则 不区分大小写(A == a)排序规则 纯 Unicode 码位顺序(文化无关)性能 比文…...
LLM大模型:Qwen3-Next-80B中的next究竟是个啥?
1、近期,国内LLM头号玩家阿里发布了Qwen3-Next-80B模型,但从名字上看就和其之前发布的模型不同:多了next!这就奇怪了:为啥会多出一个next?这个next究竟是啥意思了?2、自从3年前 chatGPT 3.5发布后,AI又开始大火,就是因为效果比传统的机器学习好10倍!效果为啥好了,核…...
中了勒索病毒 peng
中了勒索病毒 peng一,中招 早上一上班,看到电脑屏幕显示这样的壁纸。 居然中招了?不敢相信。 我发现自己的网盘里的所有文件,都被加密并改名,形如 aaaa.jpg.[[VlDy9dk2RaQ1F]].[[Ruiz@firemail.cc]].peng 而且这些文件,都已同步到了网盘,通过手机app访问,也只能看到这些…...
在 WSL 中通过 Bash 函数快速转换 Windows 路径为 Ansible/WSL 路径 - 教程
在 WSL 中通过 Bash 函数快速转换 Windows 路径为 Ansible/WSL 路径 - 教程pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Cour…...
金融租赁公司厂商租赁业务调研报告
厂商租赁金融租赁公司厂商租赁业务调研报告 报告摘要 本报告旨在全面、深入地分析中国金融租赁公司(下称“金租公司”)厂商租赁业务的现状、模式、市场环境、监管动态、数字化转型路径及绩效评估体系。截至2025年,厂商租赁作为一种深度绑定产业的业务模式,正日益成为金租公…...
普科科技PKC7030H交直流电流探头应用指南
普科PKC7030H探头支持DC-120MHz带宽、1%精度,30A连续电流测量,适用于高频大电流交直流混合信号测试。在现代电力电子、新能源及高速数字系统的设计与调试中,对复杂电流波形的精准测量是分析效率、优化性能与保障可靠性的基石。普科科技(PRBTEK)PKC7030H高频交直流电流…...
从“分散”到“统一”,中控技术利用SeaTunnel构建高效数据采集框架,核心数据同步任务0故障运行!
本文将深入探讨中控技术基于 Apache SeaTunnel 构建企业级数据采集框架的实践,重点分享集群高可用配置、性能调优、容错机制及数据质量监控等方面的具体思考与方案。作者 | 崔俊乐引言:对企业而言,数据采集的核心挑战从来不仅仅是“同步”,而是如何在大规模、多元异构的复杂…...
再见 Cursor,Qoder 真香!这波要改写 AI 编程格局
如果把未来 AI 编程工具的核心竞争力用一句话总结,那就是:能不能让开发者在透明化的协作中,信任它、依赖它,并且和它一起把项目养大。作者:loonggg 真心建议大家去使用一下这段时间最新推出的一款 AI 编程工具:Qoder 。 真的是太好用了,一点也不比 Cursor 差。 为什么这…...
T/B cell subtype marker - un
B cell ref: https://www.abcam.cn/primary-antibodies/b-cells-basic-immunophenotypingT cell ref: https://www.abcam.cn/primary-antibodies/t-cells-basic-immunophenotyping作者:un-define出处:https://www.cnblogs.com/mmtinfo/p/19099331本文版权归作者和博客园共有,…...
SAP FICO 完全凭证替代
GGB1 这个参数是获取所有行项目的关键USING bool_data TYPE gb002_015*&---------------------------------------------------------------------* *& Form u902 *&---------------------------------------------------------------------* * text *…...
K8s Application模式下的flink任务执行精要
本文分享自天翼云开发者社区《K8s Application模式下的flink任务执行精要》,作者:l****n 构键k8s集群在这里,我们需要搭建一个K8S环境用于提供flink任务的运行时环境。在这里推荐使用kubeadm或者一些脚本工具搭建,可参考本自动k8s脚本工具。具体过程在这里省略,可以参考上…...
从0打造一个TTS语音合成引擎:原理与实现
语音合成技术(Text-to-Speech, TTS)近年来发展迅猛,从早期机械感十足的合成音到如今几乎可以以假乱真的人声,背后是深度学习技术的巨大进步。本文将带你了解现代语音合成的基本原理,并尝试用Python实现一个简易版的TTS系统。 语音合成技术演进图1:语音合成技术发展历程,…...
莫队
Argvchs 说我不会根号算法,把之前的博客搬过来,然后再补点东西。 一种离线算法,可以用 \(O(n\sqrt n)\) 的复杂度处理区间查询问题,当然,也可以带修,下文也会提到。 关于复杂度 莫队优化的关键是排序 + 分块,将每个询问离线下来,按照左端点所在块从小到大排序,假如左端…...
Java基本语句-分支语句
Java基本语句-分支语句Day05 如何在API字典中寻找自己想要的Scanner类型 1.点击搜索 输入Scanner 2.字典中回显示各种类型的获取方式: nextByte()、nextShort()、nextInt()、nextLong()、nextdouble()、nextFloat()、next()多种引用使用。 3.调用Scanner类的相关方法,来获取指定…...
丘成桐谈AI
很多重要的科学发现,是在平凡的事情里面突然有个突破。 观念上的突破,在我看人工智能有困难做不到,现在全民学人工智能, 听起来很好听,但是师资不够, 跟数学的整个合作是刚开始, AI看见万千数据 记者:您第一次感觉到AI的冲击时什么时候 Yau:哈哈我坦白跟你讲,我从来没…...
异常检测在网络安全中的应用 - 实践
异常检测在网络安全中的应用 - 实践pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", monospace !important; …...
大文件分片上传
分片:// 获取文件对象const inputFile = document.querySelector(input[type="file"]);// 设置分片大小:5MBconst CHUNK_SIZE = 5 * 1024 * 1024;// 文件上传事件inputFile.onchange = async (e) => {// 获取文件信息const file = e.target.files[0];// 获取文件…...
人小鼠免疫细胞maker基因 - un
人小鼠ref:https://www.abcam.cn/primary-antibodies/immune-cell-markers-poster作者:un-define出处:https://www.cnblogs.com/mmtinfo/p/19099316本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究…...
HyperWorks许可配置
在工程设计和仿真领域,正确的软件许可配置是确保工作流程顺畅、提高生产效率和实现最佳投资回报的关键。HyperWorks作为业界领先的工程仿真软件,其灵活的许可配置功能为用户提供了广泛的定制选项,确保软件能够完全满足各种业务需求。 什么是HyperWorks许可配置? HyperWorks…...
国标GB28181视频平台EasyGBS如何解决安防视频融合与级联管理的核心痛点?
国标GB28181视频平台EasyGBS如何解决安防视频融合与级联管理的核心痛点?在平安城市、雪亮工程等大型安防项目中,如何解决不同品牌设备与平台之间的互联互通难题?本文深度解析基于国标GB/T28181协议的EasyGBS视频平台的核心特点与技术优势,阐述其如何通过标准化协议,实现大…...
python基础-推导式
1.列表推导式 : 有规律的快速创建或者控制列表1.1 创建列表 eg: list1 = [ i for i in range(10)]1.2 带条件判断的列表推导式eg: list1 = [ i for i in range(50) if i % 3 == 0]3.多个for循环实现的列表推导式eg: list1 = [(item1, item2) for item1 in list2 for item2 in…...
人 CD 抗原完全指南 - un
设立分化簇 (CD) 命名系统的目的是对白细胞表面抗原进行分类。 最初,表面抗原是根据与它们结合的对应单克隆抗体进行命名。随着各实验室逐渐发现抗原常能刺激产生多种单克隆抗体,因此需要采用一种统一的命名系统。1982 年于巴黎举行的第 1 届国际人类白细胞分化抗原专题讨论会…...
Java入门知识
Java的特性和优势 简单性 面向对象 可移植性 (“Write once ,run anywhere”) 高性能 分布式 动态性 (反射机制) 多线程 (同时进行) 安全性 (异常机制,防病毒防篡改) 健壮性 在学习过程中爱上它,能够不断主动学习 在机遇来临之前,不断健壮自己 Java的三大版本 “Wri…...
AUTOSAR网络管理
汽车行业的网络管理一般有两种,一种是AutoSar另一种是OSEK,为啥汽车要网络管理,其实是为了降低车辆电池消耗,当车辆不工作时所有总线上的ECU通讯模块或整个ECU处于低功耗状态。网络管理一般用在电池供电的ECU,比如车上CAN上的ECU。为了避免通讯错误,需要网络管理来协调网…...
写用例注意点
写用例注意点: 1、测试标题 明确测试点 2、写用例的前几条用例都是主要场景的用例先写 微信个人能发微信红包 微信群发能发拼手气红包 微信群发能发拼手气红包 微信群发能发专属气红包 3、测试标题尽量写内容不要写案例: 例如验证标题能修改密码为:6666 4、相同的模块可以进…...
12 路低延迟推流!米尔 RK3576 赋能智能安防 360 环视
在智慧城市建设加速与社区安防需求升级的双重驱动下,“360 无死角监控 + 实时响应” 已成为安防领域的核心诉求。传统监控方案常受限于摄像头接入数量不足、编解码效率低、推流延迟高三大痛点,难以覆盖社区、园区等复杂场景的全点位监控,更无法满足应急事件 “毫秒级响应” …...
A公司一面:类加载的过程是怎么样的? 双亲委派的优点和缺点? 产生fullGC的情况有哪些? spring的动态代理有哪些?区别是什么? 如何排查CPU使用率过高?
A公司一面:类加载的过程是怎么样的? 双亲委派的优点和缺点? 产生fullGC的情况有哪些? spring的动态代理有哪些?区别是什么? 如何排查CPU使用率过高?摘要 A公司的面经JVM的类加载的过程是怎么样的? 双亲委派模型的优点和缺点? 产生fullGC的情况有哪些? spring的动态代…...
Alternating Subsequence
CF1343C Alternating Subsequence 题目描述 回忆一下,如果序列 \(b\) 是序列 \(a\) 的一个子序列,那么 \(b\) 可以通过从 \(a\) 中删除零个或多个元素(不改变剩余元素的顺序)得到。例如,如果 \(a=[1, 2, 1, 3, 1, 2, 1]\),那么可能的子序列有:\([1, 1, 1, 1]\),\([3]\)…...
白鲸开源“创客北京2025”再摘殊荣,聚焦Agentic AI时代数据基础设施建设
近日,“创客北京2025”创新创业大赛海淀区级赛圆满落幕,经过最终比拼,北京白鲸开源科技有限公司凭借 「Agentic AI时代下的数据基础设施平台」(白鲸数据集成调度平台/WhaleStudio) 脱颖而出,荣获企业组二等奖。近日,“创客北京2025”创新创业大赛海淀区级赛圆满落幕,经…...
python基础-公共操作
数据类型间公共支持的操作符运算: + ,* ,in , not in‘+’ :支持的容器类型 字符串、列表、元组 ,实现两个容器的合并‘*’ : 支持的容器类型 字符串、列表、元组, 赋值容器内容str1 = q str1* 5 =qqqqqlist1 = [hello] list1*5 = [hello, hello, hello,…...
天翼云第九代弹性云主机:让每一次计算快人一步
随着数字化转型进程不断深入,云计算已成为推动千行百业智能化升级的核心引擎。弹性计算服务凭借其灵活扩展、高可用和高性能等特点,正持续为企业提供关键基础设施支持。面对日益复杂的业务场景与持续增长的计算需求,天翼云始终致力于通过持续创新和技术升级,推动弹性计算服…...
若依(RuoYi)框架漏洞总结
0x01 特征 绿若依 icon_hash=”706913071”蓝若依 icon_hash=” -1231872293”0x02 漏洞 弱口令 用户:admin ruoyi druid 密码:123456 admin druid admin123 admin888若依前台默认shiro key命令执行漏洞 若依默认使用shiro组件,所以可以试试shiro经典的remember…...
第一次个人项目作业_论文查重
第一次项目作业这个作业属于哪个课程 https://edu.cnblogs.com/campus/gdgy/Class34Grade23ComputerScience这个作业要求在哪里 https://edu.cnblogs.com/campus/gdgy/Class34Grade23ComputerScience/homework/13477这个作业的目标 实现一个3000字以上论文查重程序github连接:…...
2025年版《中科院期刊分区表》与2023年版对比表,附名单可直接查阅
2025年版《中科院期刊分区表》与2023年版相比,主要有以下几个变化: 1、发布时间提前:2025年版分区表从12月提前至3月发布,与投稿周期同步,学者可以尽早锁定期刊最新分区,避免“投稿后降区”的风险。 2、增加ESCI期刊收录:2025年版分区表增加了ESCI期刊的收录…...
对马岛之魂
护身符 稻荷神护身符----增加资源的获取 aa...
2019年双因素认证最佳实践指南
本文深入探讨2019年双因素认证的正确实现方式,对比TOTP与WebAuthn技术优劣,分析用户行为模式,并提供实际部署建议,帮助开发者构建更安全的认证系统。2019年正确实现双因素认证 - Trail of Bits博客 自3月起,Trail of Bits一直与Python软件基金会合作,为Warehouse(PyPI的…...
Account Kit(华为账号服务)再进化,开发者接入效率飙升!
Hi 各位开发者朋友~👋 为持续优化开发体验,提升集成效率,Account Kit接入体验再升级,助力构建更流畅、更安全的登录体验,让开发效率火力全开!😎 【体验升级】华为账号相关权益申请入口统一迁移至AGC华为账号一键登录权益实时审批华为账号一键登录支持三方开发框架01 …...
软件工程个人项目
软件工程个人项目3123004548软件工程个人项目这个作业属于哪个课程 <https://edu.cnblogs.com/campus/gdgy/SoftwareEngineering2024>这个作业要求在哪里 https://edu.cnblogs.com/campus/gdgy/Class34Grade23ComputerScience/homework/13477这个作业的目标 <设计一个…...