红黑树(C++)

文章目录

  • 写在前面
  • 1. 红黑树的概念及性质
    • 1. 1 红黑树的概念
    • 1. 2 红黑树的性质
  • 2. 红黑树节点的定义
  • 3. 红黑树的插入
    • 3.1 按照二叉搜索的树规则插入新节点
    • 3.2 检测新节点插入后,红黑树的性质是否造到破坏
  • 4.红黑树的删除
  • 5.红黑树的验证
  • 6.源码

写在前面

在上篇文章中,我们实现了AVL树,AVL树是一种高度平衡的二叉搜索树。通过确保任何节点的左右子树的高度差不超过1,AVL树能够维持严格的平衡状态。然而,严格平衡的代价是某些插入和删除操作可能需要多次旋转

本篇文章将实现红黑树,它是一种近似平衡的二叉搜索树。红黑树通过维持某些性质来保持树的平衡。通过这些性质,红黑树确保从根到叶子的所有路径中,最长路径不超过最短路径的两倍,从而实现近似平衡这种近似平衡比AVL树的严格平衡稍松一些,使得它在某些插入和删除操作中的效率更高,因为它需要的旋转次数较少

在接下来的内容中,我们将详细介绍红黑树的实现过程,以及它是如何通过旋转和重新着色来维护红黑树的平衡性。

1. 红黑树的概念及性质

1. 1 红黑树的概念

红黑树,是一种二叉搜索树其每个节点存储一个额外的颜色位,用于确保树的平衡性。在红黑树中,节点的颜色可以是红色或黑色通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍,因而是接近平衡的。
在这里插入图片描述

1. 2 红黑树的性质

红黑树通过以下性质来保持树的平衡:

  1. 每个节点是红色或黑色

  2. 根节点是黑色

  3. 每个叶子节点(NIL节点)是黑色
    ps:NIL节点不是传统意义上的叶子节点,而是指空节点,是用来方便我们来数路径的
    在这里插入图片描述

  4. 如果一个节点是红色,则它的两个子节点都是黑色不能出现连续的红色节点)。

  5. 从根节点到其每个叶子节点的所有路径都包含相同数量的黑色节点

为什么满足上面的性质,红黑树就能保证其最长路径中节点个数不会超过最短路径节点个数的两倍?
分析如下:
在这里插入图片描述

2. 红黑树节点的定义

TreeNode成员的几点解释:

  1. 枚举类型 Color 用于表示红黑树节点的颜色。红黑树节点只能是红色或黑色。
  2. TreeNode 是一个模板结构体,用于存储红黑树节点的信息。
    _left 指向左孩子节点,_right 指向右孩子节点,_parent 指向父节点。
    _kv 存储节点的键值对,类型为 pair<K, V>,其中 K 是键的类型,V 是值的类型。
    _col 存储节点的颜色,类型为 Color。
    构造函数初始化节点的左孩子、右孩子和父节点为空指针,键值对通过参数传递进来,并将节点颜色初始化为红色(新插入的节点默认是红色)。
enum Color
{
	RED,
	BLACK
};
template<class K, class V>
struct TreeNode
{
	TreeNode* _left; // 左孩子节点
	TreeNode* _right;// 右孩子节点
	TreeNode* _parent;// 父节点
	pair<K, V> _kv;// 存储键值对
	Color _col;  // 节点颜色
	// 构造函数
	TreeNode(const pair<K, V>& kv)
		:_left(nullptr)
		,_right(nullptr)
		,_parent(nullptr)
		,_kv(kv)
		,_col(RED)// 新插入的节点初始为红色
	{}
};

3. 红黑树的插入

红黑树就是在二叉搜索树的基础上增加了节点的颜色来控制平衡,因此红黑树也可以看成是二叉搜索树。那么红黑树的插入过程可以分为两步:

  1. 按照二叉搜索树的方式插入新节点。
  2. 检测新节点插入后,红黑树的性质是否造到破坏。

具体操作步骤如下:

3.1 按照二叉搜索的树规则插入新节点

具体操作过程参考之前写的文章,这里就不在赘述,详情参考之前写的这篇文章:搜索二叉树,这里直接给出插入新节点的具体代码。

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 (kv.first > cur->_kv.first)
		{
			parent = cur;
			cur = cur->_right;
		}
		else if (kv.first < cur->_kv.first)
		{
			parent = cur;
			cur = cur->_left;
		}
		else
		{
			return false;
		}
	}
	//插入
	cur = new Node(kv);
	if (kv.first > parent->_kv.first)
	{
		parent->_right = cur;
		cur->_parent = parent;
	}
	else
	{
		cur->_parent = parent;
		parent->_left = cur;
	}
}

3.2 检测新节点插入后,红黑树的性质是否造到破坏

这里涉及的旋转操作参考之前写的文章AVL树(C++),里面详细介绍了旋转操作,这里就不在赘述。
因为新节点的默认颜色是红色,因此:如果其双亲节点的颜色是黑色,没有违反红黑树任何性质,则不需要调整
注意:下面图片中的树有可能是一颗完整的树,也有可能是一颗局部的子树。
在这里插入图片描述

当新插入节点的双亲节点颜色为红色时,就违反了性质三不能有连在一起的红色节点,此时需要对红黑树分情况来讨论:
ps:cur为当前节点,p为父节点,g为祖父节点,u为叔叔节点。

  • 情况一: cur为红,p为红,g为黑,u存在且为红
    在这里插入图片描述
    ps:cur可能是新插入的节点,也有可能是在下面插入节点,不断往上调整,使得cur的颜色变红。

解决方式:将p,u改为黑,g改为红,然后把g当成cur,继续向上调整。
在这里插入图片描述

ps:如果g是根节点,调整完成后,需要将g改为黑色;
如果g是子树,g一定有双亲,且g的双亲如果是红色,需要继续向上调整。

在这里插入图片描述

  • 情况二: cur为红,p为红,g为黑,u不存在/u存在且为黑
    解决方式:
    cur是p的左孩子,以g为旋转点右单旋,g变红,p变黑,调整结束,无需向上调整;
    cur是p的右孩子,先以p为旋转点左单旋,再以g为旋转点右单旋,g变红,p变黑,调整结束,无需向上调整。

    在这里插入图片描述

1. 如果u节点不存在,则cur一定是新插入节点,因为如果cur不是新插入节点则cur和p一定有一个节点的颜色是黑色,就不满足性质4:每条路径黑色节点个数相同。
在这里插入图片描述
2. 如果u节点存在,则其一定是黑色的,那么cur节点原来的颜色一定是黑色的现在看到其是红色的原因是因为cur的子树在调整的过程中将cur节点的颜色由黑色改成红色。
在这里插入图片描述
p有可能是g的右孩子,这种情况下旋转操作与上面的情况相反,变色操作还是一样的,这里就不在赘述了。
如下图:
在这里插入图片描述
检测新节点插入后,红黑树的性质是否造到破坏的具体代码如下:

//插入以后判断是否满足红黑树的规则
//不满足则调整
while (parent && parent->_col == RED)
{
	Node* grandfather = parent->_parent;
	//p是g的左孩子
	if (parent == grandfather->_left)
	{
		Node* uncle = grandfather->_right;
		//u存在且为红
		if (uncle && uncle->_col == RED)
		{
			//变色+向上调整
			parent->_col = uncle->_col = BLACK;
			grandfather->_col = RED;
			cur = grandfather;
			parent = grandfather->_parent;
		}
		else
		{
			//不存在或者存在且为黑
			//旋转+变色
			//cur 是p的左 以g为旋转点右单旋
			if (cur == parent->_left)
			{
				RotateR(grandfather);
				parent->_col = BLACK;
				grandfather->_col = RED;
			}
			else
			{
				//cur 是p的右 先以p为旋转点左单旋,再以g为旋转点右单旋
				RotateL(parent);
				RotateR(grandfather);
				cur->_col = BLACK;
				grandfather->_col = RED;
			}
			break;
		}
	}
	else//p是g的右孩子
	{
		Node* uncle = grandfather->_left;
		//u存在且为红
		if (uncle && uncle->_col == RED)
		{
			//变色+向上调整
			parent->_col = uncle->_col = BLACK;
			grandfather->_col = RED;
			cur = grandfather;
			parent = grandfather->_parent;
		}
		else
		{
			//不存在或者存在且为黑
			//旋转+变色
			//cur 是p的右 以g为旋转点左单旋
			if (cur == parent->_right)
			{
				RotateL(grandfather);
				parent->_col = BLACK;
				grandfather->_col = RED;
			}
			else
			{
				//cur 是p的左 先以p为旋转点右单旋,再以g为旋转点左单旋
				RotateR(parent);
				RotateL(grandfather);
				cur->_col = BLACK;
				grandfather->_col = RED;
			}
			break;
		}
	}
}
_root->_col = BLACK;

4.红黑树的删除

红黑树的删除和AVL树一样不做深入研究,实现红黑树的插入已经足够帮助我们来了解其是如何控制平衡的,关于红黑树的删除有兴趣的可参考:《算法导论》或者《STL源码剖析》。

5.红黑树的验证

红黑树是在二叉搜索树的基础上加入了平衡性的限制,因此要验证红黑树,可以分两步:

  1. 检测其是否满足二叉搜索树
    如果中序遍历可得到一个有序的序列,就说明为二叉搜索树。
    代码如下:
void _InOrder(Node* root)
{
	if (root == nullptr) return;
	_InOrder(root->_left);
	cout << root->_kv.first << ' ';
	_InOrder(root->_right);
}
void InOrder()
{
	_InOrder(_root);
	cout << endl;
}
  1. 检测其是否满足红黑树的性质
    不能出现连续的红色节点;每条路径上的黑色节点数量相同。
bool _IsRbTree(Node* root, int blacknum, int num)
{
	if (root == nullptr)
	{
		//判断当前路径和最左路径上的黑色节点数量是否相同
		if (blacknum != num)
			return false;
		return true;
	}
	//当前节点为黑色,当前路径上的黑色节点数量加1
	if (root->_col == BLACK) num++;
	Node* parent = root->_parent;
	//判断是否出现连续的红色节点
	if (root->_col == RED && parent->_col == RED)
	{
		cout << "出现连续的红色节点";
		return false;
	}
	//递归去判断其左右子树是否满足性质
	return _IsRbTree(root->_left, blacknum, num)
		   && _IsRbTree(root->_right, blacknum, num);
}
bool IsRbTree()
{
	//空树是红黑树
	if (_root == nullptr) return true;
	if (_root->_col == RED) return false;//检查根节点的颜色
	//统计最左路径黑色节点的数量,以其为参考值,去判断每条路径上的黑色节点数量是否相同
	int blacknum = 0;
	Node* cur = _root;
	while (cur)
	{
		if (cur->_col == BLACK)
		{
			blacknum++;
		}
		cur = cur->_left;
	}
	return _IsRbTree(_root, blacknum, 0);
}

验证用例:
常规场景1 :{16, 3, 7, 11, 9, 26, 18, 14, 15}。
在这里插入图片描述
特殊场景2:{4, 2, 6, 1, 3, 5, 15, 7, 16, 14}。
在这里插入图片描述
场景三:随机生成N个数字插入验证,这里给出代码,有兴趣的可以自己去测试一下。

int main()
{
	const int N = 1000;
	srand((unsigned int)time(nullptr));
	RBTree<int, int> rbTree;
	
	for (int i = 0; i < N; ++i)
	{
		int num = rand() + i;
		rbTree.insert(make_pair(num, num));
	}
	//rbTree.InOrder();
	cout << rbTree.IsRbTree();
	return 0;
}

6.源码

#include <iostream>
using namespace std;
enum Color
{
	RED,
	BLACK
};
template<class K, class V>
struct TreeNode
{
	TreeNode* _left;
	TreeNode* _right;
	TreeNode* _parent;
	pair<K, V> _kv;
	Color _col;
	TreeNode(const pair<K, V>& kv)
		:_left(nullptr)
		,_right(nullptr)
		,_parent(nullptr)
		,_kv(kv)
		,_col(RED)
	{}
};

template<class K, class V>
class RBTree
{
	typedef TreeNode<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 (kv.first > cur->_kv.first)
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (kv.first < cur->_kv.first)
			{
				parent = cur;
				cur = cur->_left;
			}
			else
			{
				return false;
			}
		}
		//插入
		cur = new Node(kv);
		if (kv.first > parent->_kv.first)
		{
			parent->_right = cur;
			cur->_parent = parent;
		}
		else
		{
			cur->_parent = parent;
			parent->_left = cur;
		}
		//插入以后判断是否满足红黑树的规则
		//不满足则调整
		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 = grandfather->_parent;
				}
				else
				{
					if (cur == parent->_left)
					{
						RotateR(grandfather);
						parent->_col = BLACK;
						grandfather->_col = RED;
					}
					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 = grandfather->_parent;
				}
				else
				{
					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 true;
	}
	void InOrder()
	{
		_InOrder(_root);
		cout << endl;
	}
	bool IsRbTree()
	{
		if (_root == nullptr) return true;
		if (_root->_col == RED) return false;
		int blacknum = 0;
		Node* cur = _root;
		while (cur)
		{
			if (cur->_col == BLACK)
			{
				blacknum++;
			}
			cur = cur->_left;
		}
		return _IsRbTree(_root, blacknum, 0);
	}
private:
	bool _IsRbTree(Node* root, int blacknum, int num)
	{
		if (root == nullptr)
		{
			if (blacknum != num)
				return false;
			return true;
		}
		if (root->_col == BLACK) num++;
		Node* parent = root->_parent;
		if (root->_col == RED && parent->_col == RED)
		{
			cout << "出现连续的红色节点";
			return false;
		}
		return _IsRbTree(root->_left, blacknum, num)
			   && _IsRbTree(root->_right, blacknum, num);
	}
	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;
		//旋转,sub的右子树做parent的左子树
		//parent做subL的右子树
		parent->_right = subRL;
		if (subRL)
		{
			subRL->_parent = parent;
		}
		Node* pParent = parent->_parent;
		subR->_left = parent;
		parent->_parent = subR;
		//parent为根节点,需要将根节点更新为subR 
		if (parent == _root)
		{
			_root = subR;
			subR->_parent = nullptr;
		}
		else
		{
			//更新subR的父节点指针
			if (subR->_kv.first > pParent->_kv.first)
			{
				pParent->_right = subR;
			}
			else
			{
				pParent->_left = subR;
			}
			subR->_parent = pParent;
		}
	}
	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 (_root == parent)
		{
			_root = subL;
			subL->_parent = nullptr;
		}
		else
		{
			if (subL->_kv.first > pParent->_kv.first)
			{
				pParent->_right = subL;
			}
			else
			{
				pParent->_left = subL;
			}
			subL->_parent = pParent;
		}
	}
private:
	Node* _root = nullptr;
};

#include "RBTree.h"
int main()
{
	RBTree<int, int> rbTree;
	int nums[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
	for (auto e : nums)
	{
		rbTree.insert(make_pair(e, e));
	}
	rbTree.InOrder();
	cout << rbTree.IsRbTree() << endl;
	return 0;
}

至此,本片文章就结束了,若本篇内容对您有所帮助,请三连点赞,关注,收藏支持下。

创作不易,白嫖不好,各位的支持和认可,就是我创作的最大动力,我们下篇文章见!

如果本篇博客有任何错误,请批评指教,不胜感激 !!!
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/704697.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

10KM无人机高清图传通信模组,低延迟、抗干扰,飞睿智能无线MESH组网模块

随着科技的飞速发展&#xff0c;无人机技术在各个领域的应用越来越广泛。尤其在海上监测、搜索救援、货物运输等场景中&#xff0c;无人机的应用显得尤为重要。然而&#xff0c;要实现无人机在复杂海域环境中的高效通信&#xff0c;高清图传通信模组的作用不可忽视。本文将深入…

2个数据恢复助手,挽救丢失文件的得力帮手

我们的手机中存储了大量珍贵的个人信息和文件&#xff0c;一旦不慎丢失&#xff0c;往往让人心急如焚&#xff0c;想象一下&#xff0c;你正在为一个重要的项目做准备&#xff0c;突然发现手机上的所有数据都消失了&#xff0c;你会怎么办&#xff1f;这时&#xff0c;手机数据…

“全光无线星空”照亮津亚电子智能制造之路

随着第四次工业革命浪潮的到来,智能制造正成为制造业的新常态。工业4.0时代的工厂不再是封闭的制造孤岛,而是通过高度的数字化和网络化,实现生产过程的智能化、自动化和灵活化。在这样的大趋势下,制造业正经历着从传统制造向智能制造的深刻转型,数字化车间和智能化生产线成为推…

Stable Diffusion直接生成IP三视图,一天设计100个?

AI都能直接生成IP形象三视图了&#xff01; SD生成一个动物Q版IP三视图模型。标准的三视图&#xff0c;并且极富设计感&#xff0c;IP设计师的好帮手&#xff0c;用来辅助创意&#xff0c;建模参考。这个模型主要是动物类&#xff0c;一般不需堆叠复杂的质量词&#xff0c;直接…

深层网络:层数多真的更好吗?

深层网络&#xff1a;层数多真的更好吗&#xff1f; 在深度学习的世界里&#xff0c;"深度"始终是一个热门话题。随着技术的发展&#xff0c;我们有了越来越多的方法来构建更深的神经网络&#xff0c;这似乎暗示着“层数越多&#xff0c;效果越好”。然而&#xff0…

快来!AI绘画Stable Diffusion 3终于开源了,更强的文字渲染和理解力,12G显卡可跑!

大家好&#xff0c;我是设计师阿威 Stable Diffusion 3终于开源了&#xff0c;2B参数的Stable Diffusion 3 Medium模型已经可以在HuggingFace上下载了&#xff01;如无法科学上网的小伙伴我也准备好了网盘资料&#xff0c;请看文末扫描获取哦&#xff01; Stable Diffusion 3 …

每日一题——Python实现PAT甲级1116 Come on! Let‘s C(举一反三+思想解读+逐步优化)五千字好文

一个认为一切根源都是“自己不够强”的INTJ 个人主页&#xff1a;用哲学编程-CSDN博客专栏&#xff1a;每日一题——举一反三Python编程学习Python内置函数 Python-3.12.0文档解读 目录 我的写法 代码点评 时间复杂度分析 空间复杂度分析 总结 我要更强 优化思路 优化…

如何将txt文件导入Python中并进行数据处理

从文本文件中读取数据后&#xff0c;可以清洗和预处理数据&#xff0c;例如去除不必要的字符、处理缺失值等&#xff0c;以便后续的分析和建模。将文本文件导入Python并进行数据处理不仅能够有效地利用数据&#xff0c;还能通过分析和可视化来提取有用的信息和洞察&#xff0c;…

第 5 章:面向生产的 Spring Boot

在 4.1.2 节中&#xff0c;我们介绍了 Spring Boot 的四大核心组成部分&#xff0c;第 4 章主要介绍了其中的起步依赖与自动配置&#xff0c;本章将重点介绍 Spring Boot Actuator&#xff0c;包括如何通过 Actuator 提供的各种端点&#xff08;endpoint&#xff09;了解系统的…

使用el-pagination出现样式超出时加上这个属性就能轻松解决

出现的样式问题&#xff1a; 当页码数量过多时&#xff0c;多余的页码会超出我们设定的样式盒子&#xff0c;不美观 代码是这样写的&#xff0c;直接使用的el-pagination <el-paginationlayout"prev, pager, next":total"50"></el-pagination&g…

几何优化技术在AI绘画中的应用

在人工智能领域&#xff0c;艺术创作已经成为了一个重要的应用方向。通过使用深度学习和计算机视觉技术&#xff0c;我们可以创建出令人惊叹的艺术作品。然而&#xff0c;要生成高质量的图像&#xff0c;需要对模型进行大量的训练和优化。在这个过程中&#xff0c;几何优化技术…

学习Pr有哪些常见的使用的技巧?

本Premiere 学习笔记总结常见问题165条。不管你是初学的小白&#xff0c;或是刚进入剪辑学习软件阶段&#xff0c;还是说你学软件好几年了都有用处。因为这份总结涉及到了Pr的各个方面。既可以帮你解决软件出现的问题&#xff0c;也可以帮你熟知软件的实际应用&#xff0c;以及…

40. 【Java教程】数据库编程

本小节我们将学习如何使用 Java 语言结合数据库进行编程。注意&#xff0c;学习本小节需要你有一定的 SQL 基础&#xff0c;了解 MySQL 数据库的 基础 CRUD 操作。 本小节我们将选择开源免费的 MySQL 5.7 作为数据库&#xff0c;可以去官网下载并安装 MySQL。 通过本小节的学…

【Gitlab】访问默认PostgreSQL数据库

本地访问PostgreSQL gitlab有可以直接访问内部PostgreSQL的命令 sudo gitlab-rails dbconsole # 或者 sudo gitlab-psql -d gitlabhq_production效果截图 常用SQL # 查看用户状态 select id,name,email,state,last_sign_in_at,updated_at,last_credential_check_at,last_act…

【React】配置别名路径@

别名路径配置 1. 路径解析配置&#xff08;webpack&#xff09; CRA本身把webpack配置包装到了黑盒里无法直接修改&#xff0c;需要借助一个插件 - craco步骤 安装craco npm i -D craco/craco项目根目录下创建配置文件 craco.config.js配置文件中添加路径解析配置 const pa…

无法在地址[localhost]和端口[8005]上创建服务器关闭套接字(基本端口[8005]和偏移量[0])

今天小伙伴问我一个问题&#xff0c;说是新服务器启动应用&#xff0c;报了一个错误&#xff0c;如下&#xff1a; 一开始我怀疑是端口被占用 经过排查端口没有被占用&#xff0c;然后我怀疑localhost解析有问题 经过 ping localhost 后&#xff0c;得到以下结果到这里很明…

基于若依的ruoyi-nbcio-plus里抄送人多页选择人员的bug修复

更多ruoyi-nbcio功能请看演示系统 gitee源代码地址 前后端代码&#xff1a; https://gitee.com/nbacheng/ruoyi-nbcio 演示地址&#xff1a;RuoYi-Nbcio后台管理系统 http://218.75.87.38:9666/ 更多nbcio-boot功能请看演示系统 gitee源代码地址 后端代码&#xff1a; h…

阿里云香港服务器怎么样?

大家都知道阿里云是国内最受欢迎的云服务商&#xff0c;那么阿里云香港服务器究竟怎么样呢&#xff1f;和硅云的香港服务器用于做外贸网站等业务相比各有哪些优缺点呢&#xff1f; 阿里云和硅云在香港云服务领域有着广泛的应用和良好的口碑。然而&#xff0c;它们各自的特点和…

短剧cps平台有哪些,短剧cps系统搭建

一、短剧CPS平台主要包括以下几个&#xff1a; 1&#xff0c;快手平台 拥有庞大的用户基础和活跃的短剧消费群体&#xff0c;通过快手的推广&#xff0c;短剧可以获得更多的曝光。同时&#xff0c;快手还提供CPS分佣模式。 2&#xff0c;抖音平台 作为国内最大的短视频平台…

我国的AI大模型前景如何?

目前&#xff0c;我国的AI大模型市场前景很好。这个产业的发展源于多领域的广泛需求&#xff0c;包括办公、制造、金融、医疗、政务等场景中的降本增效、生产自动化、降低风险、提高诊断准确率、提高政务服务效率等诉求。这些领域的创新和发展共同推动了我国AI大模型产业的蓬勃…