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

C++ 红黑树实现详解:理论+代码+图解

C++ 红黑树实现详解:理论+代码+图解


文章目录

  • C++ 红黑树实现详解:理论+代码+图解
  • 一、红黑树的概念
    • 1.1 红黑树的规则
    • 1.2 红黑树的效率
    • 1.3 红黑树和AVL树的比较
  • 二、红黑树的实现
    • 2.1 红黑树的结构
    • 2.2 红黑树的插入
      • 2.2.1 插入结点是根结点
      • 2.2.2 插入结点的叔叔是红色
      • 2.2.3 插入结点的叔叔是黑色
        • 2.2.3.1 LL型
        • 2.2.3.2 RR型
        • 2.2.3.3 RL型
        • 2.2.3.4 LR型
      • 2.2.4 红黑树的插入代码实现
    • 2.3 红黑树的查找
    • 2.4 红黑树的验证
    • 2.5 红黑树的删除
      • 2.5.1 只有左孩子/右孩子:代替后变黑
      • 2.5.2 没有孩子
        • 2.5.2.1 没有孩子: 删除红节点
        • 2.5.2.2 没有孩子: 删除黑节点
          • 2.5.2.2.1 没有孩子: 删除黑节点(兄弟是黑色)
            • 2.5.2.2.1.1 没有孩子: 删除黑节点(兄弟是黑色)兄弟至少有一个红孩子
            • 第一种情况:LL 变色+旋转
            • 第二种情况:RR 变色+旋转
            • 第三种情况:LR 变色+旋转
            • 第四种情况:RL 变色+旋转
            • 2.5.2.2.1.2 没有孩子: 删除黑节点(兄弟是黑色)兄弟的孩子都是黑色
            • (1)第一种情况
            • (2)第二种情况
            • (3)第三种情况
          • 2.5.2.2.2 没有孩子: 删除黑节点(兄弟是红色)
      • 2.5.3 删除的代码实现
  • 三、源代码总结


一、红黑树的概念

在这里插入图片描述


1.1 红黑树的规则

在这里插入图片描述

在这里插入图片描述


在这里插入图片描述


在这里插入图片描述

1.2 红黑树的效率

在这里插入图片描述在这里插入图片描述


1.3 红黑树和AVL树的比较

在这里插入图片描述在这里插入图片描述


二、红黑树的实现

2.1 红黑树的结构

这里用枚举值表示颜色,默认按key/value结构实现,更新控制平衡也要加入parent指针!


在这里插入图片描述


2.2 红黑树的插入

红黑树插入节点和二叉搜索树是一样的,但是红黑树有红黑两种颜色标识
红黑树的插入:关键看叔叔uncle

在这里插入图片描述


在这里插入图片描述


下面介绍如果红黑树的结构被破坏了,需要做的以下三种调整!


2.2.1 插入结点是根结点

这里会违反根叶黑的性质,直接把根节点变黑即可
在这里插入图片描述


2.2.2 插入结点的叔叔是红色

对cur的父亲、叔叔、爷爷三个节点进行变色(红变黑,黑变红)
随后让爷爷变成cur插入节点:cur指向爷爷,进一步向上判定是否违反红黑树性质
对于插入节点的叔叔是红色,可能不止调整一次,每次调整完,都需要跳转到爷爷那继续判定调整

在这里插入图片描述在这里插入图片描述


2.2.3 插入结点的叔叔是黑色

插入节点的叔叔是黑色:(LL,RR,LR,RL)旋转,然后变色


2.2.3.1 LL型

在这里插入图片描述


2.2.3.2 RR型

在这里插入图片描述


2.2.3.3 RL型

在这里插入图片描述
在这里插入图片描述


2.2.3.4 LR型

在这里插入图片描述
在这里插入图片描述


2.2.4 红黑树的插入代码实现

旋转代码的实现跟AVL树是一样的,只是不需要更新平衡因子


在这里插入图片描述
g)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


代码如下(示例):

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);// 新增结点。颜⾊红⾊给红⾊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;// g// p uif (parent == grandfather->_left){Node* uncle = grandfather->_right;if (uncle && uncle->_col == RED){// u存在且为红 -》变⾊再继续往上处理parent->_col = uncle->_col = BLACK;grandfather->_col = RED;cur = grandfather;parent = cur->_parent;}else{// u存在且为⿊或不存在 -》旋转+变⾊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 pNode* 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// cif (cur == parent->_right){RotateL(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{// g// u p// cRotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}}_root->_col = BLACK;return true;} 

2.3 红黑树的查找

按二叉搜索树逻辑实现即可,搜索效率为O(logN)
在这里插入图片描述


2.4 红黑树的验证

在这里插入图片描述


在这里插入图片描述
在这里插入图片描述


代码如下(示例):

	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);} 

2.5 红黑树的删除

红黑树的删除主要采用了二叉搜索树的方法
在这里插入图片描述


2.5.1 只有左孩子/右孩子:代替后变黑

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


在这里插入图片描述在这里插入图片描述
在这里插入图片描述


2.5.2 没有孩子

2.5.2.1 没有孩子: 删除红节点

在没有孩子的情况下,如果删除红节点:删除后无需任何调整


在这里插入图片描述


2.5.2.2 没有孩子: 删除黑节点

为了更好理解这个过程,我们这里引入了双黑节点的概念
因为删除黑色节点之后,所有经过他的路径都会少一个黑色节点,假设这个节点具有两层黑色
这样我们后面调整的任务就是将这个双黑节点消除或者让他变成单黑节点


2.5.2.2.1 没有孩子: 删除黑节点(兄弟是黑色)
2.5.2.2.1.1 没有孩子: 删除黑节点(兄弟是黑色)兄弟至少有一个红孩子

这里注意:LL和RR是同种类型,LR和RL也是同一种类型


第一种情况:LL 变色+旋转

在这里插入图片描述

在这里插入图片描述在这里插入图片描述


第二种情况:RR 变色+旋转

在这里插入图片描述
在这里插入图片描述在这里插入图片描述


第三种情况:LR 变色+旋转

在这里插入图片描述
在这里插入图片描述在这里插入图片描述


第四种情况:RL 变色+旋转

在这里插入图片描述
在这里插入图片描述在这里插入图片描述


2.5.2.2.1.2 没有孩子: 删除黑节点(兄弟是黑色)兄弟的孩子都是黑色

在这里插入图片描述

在这里插入图片描述

(1)第一种情况

在这里插入图片描述
在这里插入图片描述在这里插入图片描述


在这里插入图片描述在这里插入图片描述


在这里插入图片描述
在这里插入图片描述在这里插入图片描述


(2)第二种情况

在这里插入图片描述在这里插入图片描述在这里插入图片描述


(3)第三种情况

在这里插入图片描述
在这里插入图片描述


2.5.2.2.2 没有孩子: 删除黑节点(兄弟是红色)

在这里插入图片描述


在这里插入图片描述

在这里插入图片描述


在这里插入图片描述
在这里插入图片描述


在这里插入图片描述
在这里插入图片描述


2.5.3 删除的代码实现

代码如下(示例):

//删除函数
bool Erase(const K& key)
{//(一)找到删除节点Node* cur = Find(key);//未找到返回falseif (cur == nullptr){return false;}//记录父节点Node* parent = cur->_parent;//用于标记实际的待删除结点及其父结点Node* delParent = nullptr;Node* delCur = nullptr;if (cur->_left == nullptr) //待删除结点的左子树为空{if (cur == _root) //待删除结点是根结点{_root = _root->_right; //让根结点的右子树作为新的根结点if (_root){_root->_parent = nullptr;_root->_col = BLACK; //根结点为黑色}delete cur; //删除原根结点return true;}else{delParent = parent; //标记实际删除结点的父结点delCur = cur; //标记实际删除的结点}}else if (cur->_right == nullptr) //待删除结点的右子树为空{if (cur == _root) //待删除结点是根结点{_root = _root->_left; //让根结点的左子树作为新的根结点if (_root){_root->_parent = nullptr;_root->_col = BLACK; //根结点为黑色}delete cur; //删除原根结点return true;}else{delParent = parent; //标记实际删除结点的父结点delCur = cur; //标记实际删除的结点}}else //待删除结点的左右子树均不为空{//替换法删除//寻找待删除结点右子树当中key值最小的结点作为实际删除结点Node* minParent = cur;Node* minRight = cur->_right;while (minRight->_left){minParent = minRight;minRight = minRight->_left;}cur->_kv = minRight->_kv; //将待删除结点的键值改为minRight的键值delParent = minParent; //标记实际删除的父节点delCur = minRight; //标记实际删除的结点}//记录待删除结点及其父结点,便于后面删除Node* del = delCur;Node* delP = delParent;//(二)调整红黑树AdjustRBTree(delCur, delParent);//(三)进行实际删除DeleteNode(del, delP);return true;
}
void DeleteNode(Node* del, Node* delP)
{if (del->_left == nullptr) //实际删除结点的左子树为空{if (del == delP->_left) //实际删除结点是其父结点的左孩子{delP->_left = del->_right;//指向父节点if (del->_right)del->_right->_parent = delP;}else //实际删除结点是其父结点的右孩子{delP->_right = del->_right;if (del->_right)del->_right->_parent = delP;}}else //实际删除结点的右子树为空{if (del == delP->_left) //实际删除结点是其父结点的左孩子{delP->_left = del->_left;if (del->_left)del->_left->_parent = delP;}else //实际删除结点是其父结点的右孩子{delP->_right = del->_left;if (del->_left)del->_left->_parent = delP;}}delete del; //实际删除结点
}
void AdjustRBTree(Node* delCur, Node* delParent)
{if (delCur->_col == BLACK) //删除的是黑色结点{if (delCur->_left) //待删除结点有一个红色的左孩子(不可能是黑色){delCur->_left->_col = BLACK; //将这个红色的左孩子变黑即可}else if (delCur->_right) //待删除结点有一个红色的右孩子(不可能是黑色){delCur->_right->_col = BLACK; //将这个红色的右孩子变黑即可}else //待删除结点的左右均为空{while (delCur != _root) //可能一直调整到根结点{if (delCur == delParent->_left) //待删除结点是其父结点的左孩子{Node* brother = delParent->_right; //兄弟结点是其父结点的右孩子//情况一:brother为红色if (brother->_col == RED){delParent->_col = RED;brother->_col = BLACK;RotateL(delParent);//需要继续处理brother = delParent->_right; //更新brother}//情况二:brother为黑色,且其左右孩子都是黑色结点或为空if (((brother->_left == nullptr) || (brother->_left->_col == BLACK))&& ((brother->_right == nullptr) || (brother->_right->_col == BLACK))){brother->_col = RED;if (delParent->_col == RED){delParent->_col = BLACK;break;}//需要继续处理delCur = delParent;delParent = delCur->_parent;}else{//情况三:brother为黑色,且其左孩子是红色结点,右孩子是黑色结点或为空if ((brother->_right == nullptr) || (brother->_right->_col == BLACK)){brother->_left->_col = BLACK;brother->_col = RED;RotateR(brother);//需要继续处理brother = delParent->_right; //更新brother}//情况四:brother为黑色,且其右孩子是红色结点brother->_col = delParent->_col;delParent->_col = BLACK;brother->_right->_col = BLACK;RotateL(delParent);break; //情况四执行完毕后调整一定结束}}else //delCur == delParent->_right //待删除结点是其父结点的右孩子{Node* brother = delParent->_left; //兄弟结点是其父结点的左孩子//情况一:brother为红色if (brother->_col == RED) //brother为红色{delParent->_col = RED;brother->_col = BLACK;RotateR(delParent);//需要继续处理brother = delParent->_left; //更新brother}//情况二:brother为黑色,且其左右孩子都是黑色结点或为空if (((brother->_left == nullptr) || (brother->_left->_col == BLACK))&& ((brother->_right == nullptr) || (brother->_right->_col == BLACK))){brother->_col = RED;if (delParent->_col == RED){delParent->_col = BLACK;break;}//需要继续处理delCur = delParent;delParent = delCur->_parent;}else{//情况三:brother为黑色,且其右孩子是红色结点,左孩子是黑色结点或为空if ((brother->_left == nullptr) || (brother->_left->_col == BLACK)){brother->_right->_col = BLACK;brother->_col = RED;RotateL(brother);//需要继续处理brother = delParent->_left; //更新brother}//情况四:brother为黑色,且其左孩子是红色结点brother->_col = delParent->_col;delParent->_col = BLACK;brother->_left->_col = BLACK;RotateR(delParent);break; //情况四执行完毕后调整一定结束}}}}}
}

三、源代码总结

代码如下(示例):

#include<iostream>
using namespace std;
#include<string>// 枚举值表⽰颜⾊
enum Colour
{RED,BLACK
};// 这⾥我们默认按key/value结构实现
template<class K, class V>
struct RBTreeNode
{// 这⾥更新控制平衡也要加⼊parent指针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:// ......// 旋转代码的实现跟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->_right;}else if (cur->_kv.first > kv.first){parent = cur;cur = cur->_left;}else{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;// g// p uif (parent == grandfather->_left){Node* uncle = grandfather->_right;if (uncle && uncle->_col == RED){// u存在且为红 -》变⾊再继续往上处理parent->_col = uncle->_col = BLACK;grandfather->_col = RED;cur = grandfather;parent = cur->_parent;}else{// u存在且为⿊或不存在 -》旋转+变⾊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 pNode* 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// cif (cur == parent->_right){RotateL(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{// g// u p// cRotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}}_root->_col = BLACK;return true;}Node* Find(const K& key){Node* cur = _root;while (cur){if (cur->_kv.first < key){cur = cur->_right;}else if (cur->_kv.first > key){cur = cur->_left;}else{return cur;}}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);}//删除函数bool Erase(const K& key){//(一)找到删除节点Node* cur = Find(key);//未找到返回falseif (cur == nullptr){return false;}//记录父节点Node* parent = cur->_parent;//用于标记实际的待删除结点及其父结点Node* delParent = nullptr;Node* delCur = nullptr;if (cur->_left == nullptr) //待删除结点的左子树为空{if (cur == _root) //待删除结点是根结点{_root = _root->_right; //让根结点的右子树作为新的根结点if (_root){_root->_parent = nullptr;_root->_col = BLACK; //根结点为黑色}delete cur; //删除原根结点return true;}else{delParent = parent; //标记实际删除结点的父结点delCur = cur; //标记实际删除的结点}}else if (cur->_right == nullptr) //待删除结点的右子树为空{if (cur == _root) //待删除结点是根结点{_root = _root->_left; //让根结点的左子树作为新的根结点if (_root){_root->_parent = nullptr;_root->_col = BLACK; //根结点为黑色}delete cur; //删除原根结点return true;}else{delParent = parent; //标记实际删除结点的父结点delCur = cur; //标记实际删除的结点}}else //待删除结点的左右子树均不为空{//替换法删除//寻找待删除结点右子树当中key值最小的结点作为实际删除结点Node* minParent = cur;Node* minRight = cur->_right;while (minRight->_left){minParent = minRight;minRight = minRight->_left;}cur->_kv = minRight->_kv; //将待删除结点的键值改为minRight的键值delParent = minParent; //标记实际删除的父节点delCur = minRight; //标记实际删除的结点}//记录待删除结点及其父结点,便于后面删除Node* del = delCur;Node* delP = delParent;//(二)调整红黑树AdjustRBTree(delCur, delParent);//(三)进行实际删除DeleteNode(del, delP);return true;}void DeleteNode(Node* del, Node* delP){if (del->_left == nullptr) //实际删除结点的左子树为空{if (del == delP->_left) //实际删除结点是其父结点的左孩子{delP->_left = del->_right;//指向父节点if (del->_right)del->_right->_parent = delP;}else //实际删除结点是其父结点的右孩子{delP->_right = del->_right;if (del->_right)del->_right->_parent = delP;}}else //实际删除结点的右子树为空{if (del == delP->_left) //实际删除结点是其父结点的左孩子{delP->_left = del->_left;if (del->_left)del->_left->_parent = delP;}else //实际删除结点是其父结点的右孩子{delP->_right = del->_left;if (del->_left)del->_left->_parent = delP;}}delete del; //实际删除结点}void AdjustRBTree(Node* delCur, Node* delParent){if (delCur->_col == BLACK) //删除的是黑色结点{if (delCur->_left) //待删除结点有一个红色的左孩子(不可能是黑色){delCur->_left->_col = BLACK; //将这个红色的左孩子变黑即可}else if (delCur->_right) //待删除结点有一个红色的右孩子(不可能是黑色){delCur->_right->_col = BLACK; //将这个红色的右孩子变黑即可}else //待删除结点的左右均为空{while (delCur != _root) //可能一直调整到根结点{if (delCur == delParent->_left) //待删除结点是其父结点的左孩子{Node* brother = delParent->_right; //兄弟结点是其父结点的右孩子//情况一:brother为红色if (brother->_col == RED){delParent->_col = RED;brother->_col = BLACK;RotateL(delParent);//需要继续处理brother = delParent->_right; //更新brother}//情况二:brother为黑色,且其左右孩子都是黑色结点或为空if (((brother->_left == nullptr) || (brother->_left->_col == BLACK))&& ((brother->_right == nullptr) || (brother->_right->_col == BLACK))){brother->_col = RED;if (delParent->_col == RED){delParent->_col = BLACK;break;}//需要继续处理delCur = delParent;delParent = delCur->_parent;}else{//情况三:brother为黑色,且其左孩子是红色结点,右孩子是黑色结点或为空if ((brother->_right == nullptr) || (brother->_right->_col == BLACK)){brother->_left->_col = BLACK;brother->_col = RED;RotateR(brother);//需要继续处理brother = delParent->_right; //更新brother}//情况四:brother为黑色,且其右孩子是红色结点brother->_col = delParent->_col;delParent->_col = BLACK;brother->_right->_col = BLACK;RotateL(delParent);break; //情况四执行完毕后调整一定结束}}else //delCur == delParent->_right //待删除结点是其父结点的右孩子{Node* brother = delParent->_left; //兄弟结点是其父结点的左孩子//情况一:brother为红色if (brother->_col == RED) //brother为红色{delParent->_col = RED;brother->_col = BLACK;RotateR(delParent);//需要继续处理brother = delParent->_left; //更新brother}//情况二:brother为黑色,且其左右孩子都是黑色结点或为空if (((brother->_left == nullptr) || (brother->_left->_col == BLACK))&& ((brother->_right == nullptr) || (brother->_right->_col == BLACK))){brother->_col = RED;if (delParent->_col == RED){delParent->_col = BLACK;break;}//需要继续处理delCur = delParent;delParent = delCur->_parent;}else{//情况三:brother为黑色,且其右孩子是红色结点,左孩子是黑色结点或为空if ((brother->_left == nullptr) || (brother->_left->_col == BLACK)){brother->_right->_col = BLACK;brother->_col = RED;RotateL(brother);//需要继续处理brother = delParent->_left; //更新brother}//情况四:brother为黑色,且其左孩子是红色结点brother->_col = delParent->_col;delParent->_col = BLACK;brother->_left->_col = BLACK;RotateR(delParent);break; //情况四执行完毕后调整一定结束}}}}}}private:Node* _root = nullptr;
};

http://www.dtcms.com/a/321614.html

相关文章:

  • 主流多模态大模型使用总结
  • GPT-5测评:AI新纪元的开启还是炒作?
  • 【SpringBoot】01 基础入门-SpringBoot2:从核心技术到响应式编程
  • Jenkins自动化构建部署Java、Web前后端项目
  • 使用Python将中文语音翻译成英语音频
  • 达梦DISQL执行SQL和SQL脚本
  • 医疗数据中台架构实战:Java实现高可用、低耦合的数据治理方案
  • 30人大型视频会议设备清单
  • 零基础小白如何使用QGIS制作研究区地形区位图教程
  • 参数服务器 server and client
  • 一文可视化分析2025年6月计算机视觉顶刊IJCV前沿热点
  • 满足高性能AI服务器的企业SSD有哪些?三星PM1743与Solidigm PS1010
  • Ⅹ—6.计算机二级综合题27---30套
  • 研发流程管理经验分享
  • 部署ELK8.18对日志进行收集、展示
  • 1Panel Agent 证书绕过实现远程命令执行漏洞复现(CVE-2025-54424)
  • 【Spring Boot 快速入门】八、登录认证
  • Java 大视界 -- Java 大数据机器学习模型在金融风险传染路径模拟与防控策略制定中的应用(387)
  • [Oracle] LEAST()函数
  • CORS 跨域问题 Next.js 跨域问题放通
  • HttpURLConnection (JDK原生)和Hutool HTTP工具的区别
  • GStreamer:开源多媒体框架技术详解与应用实践
  • c++初学day1(类比C语言进行举例,具体原理等到学到更深层的东西再进行解析)
  • 从0配置yolo实例分割(ubuntu)
  • 五种IO模型与⾮阻塞IO
  • 无人机仿真环境搭建
  • 二叉树实现
  • 【科研绘图系列】R语言绘制气泡图
  • [LLM 应用评估] 提示词四大要素 | 评估样本_单次交互快照 | 数据集 | Hugging Face集成
  • 杰理ac791 [Info]: [LL_S]Recv - LL_CHANNEL_MAP_REQ