红黑树的插入和删除

news2024/11/25 13:38:09

红黑树(C++)

  • 红黑树简述
    • 红黑树的概念
    • 红黑树的性质
    • 红黑树结点定义
  • 一,红黑树的插入
    • 插入调整
    • 插入代码
  • 二,红黑树的验证
  • 三,红黑树的删除
    • 待删除的结点只有一个子树
      • 删除结点颜色为红色
      • 删除结点颜色为黑色
    • 删除的结点为叶子节点
      • 删除结点颜色为红色
      • 删除结点颜色为黑色
    • 红黑树删除代码
  • 四,红黑树与AVL树的比较

红黑树简述

红黑树的概念

🚀红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍,因而是接近平衡的。

红黑树的性质

  1. 每个结点不是红色就是黑色
  2. 根节点是黑色的
  3. 如果一个节点是红色的,则它的两个孩子结点是黑色的
  4. 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均 包含相同数目的黑色结点
  5. 每个叶子结点都是黑色的(此处的叶子结点指的是空结点,也叫做NIL结点)

🚀根据红黑树的性质,可以得到红黑树最长路径的长度不超过最短路径长度的2倍,所以红黑树不是高度平衡的,也就意味着它的查询效率一定不如AVL树,因为AVL树的高度平衡的。但是红黑树的查询效率也不低,假设有一颗N个结点的AVL树,那么树的高度就是logN。但如果有一颗N个结点的红黑树,假设黑色结点为N - i个(黑色结点的个数肯定是小于等于N的)那么红黑树一条最短的路径为log(N - i)接近于logN,红黑树最长的一条路径假设为最短路径的2倍,2log(N - i),接近于2logN。
假设N为10亿,对于AVL树来说查找30次即可,对于红黑树需要60次(实际中肯定是小于60的),可见它们查找的性能相近。

红黑树结点定义

🚀树的接结点的定义,采用三指针结构(左子树指针,右子树指针,parent指针),存储的内容还有结点的颜色和pair结构

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

一,红黑树的插入

🚀 根据红黑树的规则,我们知道插入新的结点,最好插入红色的结点,因为黑色节点一定会导致违反每条路径上的黑色节点的数目相同的规则。
🚀 红黑树的插入主要分四种情况:
1,父节点为黑色。
2,父节节点为红色,叔叔结点存在且为红色。
2,父亲节点为红色,叔叔结点存在且为黑色。
4,父亲节点为红色,叔叔结点不存在。

插入调整

🚀情况1:父亲结点为黑色,那么不需要调整。
🚀情况2:父亲节点为红色,叔叔结点存在且为红色。
在这里插入图片描述
解决方法:
1,parent结点变黑,uncle结点变黑
2,grandfather结点变红,继续向上调整(由于grandfather变成了红色,ppnode结点如果为红色还需继续调整)

向上调整过程中可能一致调整到根节点,但是根节点必须是黑色,那么就相当于每条路径多了一个黑色结点。

注: p结点为pp结点的右子树时,调整方法类似,就不再叙述。

🚀情况3:(1)父亲结点为红色,叔叔结点存在且为黑色。且cur为父亲结点左子树。

在这里插入图片描述
解决方法:
1,parent结点颜色变黑,grandfather结点的颜色变红
2,以grandfather结点为根的子树右旋

在这里插入图片描述
🚀情况3:(2)父亲结点为红色,叔叔结点存在且为黑色。且cur为父亲结点右子树。

在这里插入图片描述
解决方法:
1,对parent为根节点的子树进行左旋,转化为(1)情况
2,此时cur结点变黑,grandfather结点变红
3,以grandfather结点为根的子树右旋

在这里插入图片描述
情况3中的两种情况,必然是通过情况2向上调整过程中形成的,因为如果不是向上调整过程中形成的话,那么cur原本的位置就是NULL,那么这条路径上的黑色结点不然比叔叔结点所在路径的黑色节点要少。

注: parent结点为grandfather结点的右子树情况与上述情况类似,就不再叙述。

🚀情况4:(1)父亲节点为红色,且没有叔叔结点。且cur结点为父亲的左子树

在这里插入图片描述
解决方法:
1,parent结点变黑,grandfather结点变红
2,以grandfather结点为根节点的子树右旋

在这里插入图片描述
🚀情况4:(2)父亲节点为红色,且没有叔叔结点。且cur结点为父亲的右子树

在这里插入图片描述

解决方法:
1,以parent为根节点的子树左旋
2,cur结点变黑,grandfather结点变红
3,以grandfather结点为根节点的子树右旋

在这里插入图片描述
注: parent结点为grandfather结点右子树的情况与上述情况类似,不再叙述。

插入代码

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 (cur->_kv.first > kv.first)
		{
			parent = cur;
			cur = cur->_left;
		}
		else if (cur->_kv.first < kv.first)
		{
			parent = cur;
			cur = cur->_right;
		}
		else
		{
			//已经存在了
			return false;
		}
	}
	//找到了属于它的位置,插入
	cur = new node(kv);
	cur->_parent = parent;
	if (kv.first < parent->_kv.first)
	{
		parent->_left = cur;
	}
	else
	{
		parent->_right = cur;
	}
	//只要parent结点存在,并且其结点为颜色为红色就需要调整
	while (parent && parent->_col == RED)
	{
		node* grandfather = parent->_parent;
		if (parent == grandfather->_left)
		{
			//1,第一种情况,存在叔叔结点,并且为红色
			if (grandfather->_right && grandfather->_right->_col == RED)
			{
				//祖父结点变红,父亲结点和叔叔结点变黑,继续向上调整,直到根节点为止。
				grandfather->_col = RED;
				parent->_col = BLACK;
				grandfather->_right->_col = BLACK;
				cur = grandfather;
				parent = cur->_parent;
			}
			//2,叔叔结点不存在或者为黑色
			else
			{
				//2.1 cur为parent的左子树,那么只需要祖父为根的树右旋即可
				if (cur == parent->_left)
				{
					grandfather->_col = RED;
					parent->_col = BLACK;
					RotateR(grandfather);
				}
				//2.2 cur为parent的右子树,那么先对parent左旋,在对祖父右旋
				else if (cur == parent->_right)
				{
					grandfather->_col = RED;
					cur->_col = BLACK;
					RotateLR(grandfather);
				}
				break;
			}
		}
		else
		{
			if (grandfather->_left && grandfather->_left->_col == RED)
			{
				grandfather->_col = RED;
				parent->_col = BLACK;
				grandfather->_left->_col = BLACK;
				cur = grandfather;
				parent = cur->_parent;
			}
			else
			{
				if (cur == parent->_right)
				{
					grandfather->_col = RED;
					parent->_col = BLACK;
					RotateL(grandfather);
				}
				else if (cur == parent->_left)
				{
					grandfather->_col = RED;
					cur->_col = BLACK;
					RotateRL(grandfather);
				}
				break;
			}
		}
	}
	_root->_col = BLACK;
	return true;
}

二,红黑树的验证

🚀红黑树的验证就是验证红黑树的5条性质:显然1 , 2, 5 是不需要验证的,只需要验证:每条路径上的黑色节点的数目一致,和没有连续的红色结点的性质即可。

bool IsBalance()
{
	if (_root == nullptr)
	{
		return true;
	}
	if (_root->_col == RED)
	{
		cout << "违反规则1,根节点的颜色为黑色" << endl;
		return false;
	}
	size_t blackCount = 0;
	node* cur = _root;
	while (cur)
	{
		if (cur->_col == BLACK)
			blackCount++;
		cur = cur->_left;
	}

	return _IsBalance(_root, 0, blackCount);
}
bool _IsBalance(node* root, size_t k, size_t blackCount)
{
	if (root == nullptr)
	{
		if (k != blackCount)
		{
			cout << "违反规则4,每条路径具有相同的黑色节点" << endl;
			return false;
		}
		return true;
	}
	if (root->_col == BLACK)
	{
		k++;
	}
	if (root->_col == RED && root->_parent && root->_parent->_col == RED)
	{
		cout << "违反性质3,存在相邻的红色结点" << endl;
		return false;
	}
	return _IsBalance(root->_left, k, blackCount) &&
		_IsBalance(root->_right, k, blackCount);
}

🚀由于,函数的返回值必须是bool类型,但是我们还要计算每条路径上的黑色结点的个数,所以我们可以先计算出一条路径上黑色结点的个数blackcount,然后以参数的形式传给函数。

三,红黑树的删除

🚀红黑树删除结点的逻辑大体分三步:
1,找到要删除的结点
2,调整红黑树的平衡
3,删除结点

🚀红黑树的删除大体上分为两种情况:
1,删除的结点是叶子结点
2,删除的结点只有左子树或者只有右子树

注: 对于一颗二叉搜索树,当要删除的结点既有左子树又有右子树,通常是找一个替代结点(要删除结点的右子树中值最小的结点,或者左子树中值最大的结点),将要删除的结点的值改为替代结点的值,然后删除替代节点即可,这样就转化为上述的两种情况。

待删除的结点只有一个子树

🚀首先,我们先讨论删除的结点只有一个子树的情况。

删除结点颜色为红色

当删除的结点是红色结点的时候,其实只有一种情况:其左子树或者右子树为红色。
在这里插入图片描述
对于这种情况直接删除待删除的结点即可,不需要调整。

注: :红黑树的规则之一是:每条路径的黑色结点的数目相同,如果待删除的结点的子树为黑色时,那么就会使得每条路径的黑色结点数目不一致。所以这种情况是不存在的,图示:

在这里插入图片描述

删除结点颜色为黑色

这种情况与删除结点为红色时相似,只有一种情况:删除结点的左子树或者右子树为红色。
在这里插入图片描述
对于这种情况,不需要调整,直接删除即可。

注: 待删除结点的左子树为黑色或者右子树为黑色的时候,这种场景是不存在的,违反了红黑树每条路径的黑色节点的数目一致的规则。图示:

在这里插入图片描述
下面,再谈删除的结点为叶子结点的情况。

删除的结点为叶子节点

删除结点颜色为红色

删除结点为叶子结点,并且其颜色是红色那么说明删除这个节点并不会影响红黑树的平衡,所以不用做后序调整,直接删除即可。

在这里插入图片描述

删除结点颜色为黑色

🚀删除结点颜色为黑色,这种情况是最复杂的,其还会分为四种小情况。
注意: 由于情况比较复杂,下面的讲述就是以删除结点为其父节点的左子树的情况,为父节点右子树的情况与此很类似,自行脑补。
1,brother结点为红色。
在这里插入图片描述
删除黑色结点必然会使此路径上黑色节点减一导致红黑树不再平衡,所以需要调整。调整的方式为:bro结点颜色变黑,parent结点颜色变红,parent结点左旋。之后就会变成后序的情况。
在这里插入图片描述

注: 情况1是如何变成情况2 3 4的:
(1).当bro结点的左子树存在(记作left)且为黑色并且left的左右子树都为黑色或者左右子树都为空->情况2
(2).当bro结点的左子树存在(记作left)且为黑色并且left的左子树为红色,left右子树存在且为黑色或者left右子树不存在->情况3
(3).当bro结点的左子树存在(记作left)且为黑色并且left的右子树为红色->情况4

2,brother结点为黑色,且其左右子树都是黑色或者左右子树都为空。
(1)parent节点是红色
在这里插入图片描述

对于这种情况需要将bro结点变为红色,parent结点变为黑色,这样在局部的这颗子树内部使其重新恢复平衡,直接返回(代码中就是break,因为后面还要做删除结点的工作),不用再做调整。

在这里插入图片描述

(2)parent节点的黑色

在这里插入图片描述

对于parent结点是黑色的情况,同样需要将bro结点颜色变红,这样才能使得parent结点的左右子树平衡(每条路径上黑色结点的数目一致)。但是在这颗局部的树中不能使得红黑树重新恢复平衡,所以需要继续向上调整(del = del->parent)
注意: 这里的继续向上调整,让del = del->parent不是说将要删除的结点修改,因为现在做的是调整过程不是删除过程, 让del = del->parent意思是以del->parent为根的子树的每条路径上黑色结点的数目比其他路径上的数目要少,要继续调整。(这很重要,不要理解错误)。并且其向上调整时可能转化为 1 2 3 4 中其中任意一种。

在这里插入图片描述

注: 红色圆圈没有上色表示颜色任意。

3,brother结点为黑色,且其左孩子为红色,右孩子为黑色,或者有孩子为空。

在这里插入图片描述
首先将bro的颜色改为红色,将bro的左子树的颜色改为黑色,然后对bro为根的子树进行一次右旋。这种情况就会转化为下面的情况4。
在这里插入图片描述

4,brother结点为黑色,且其右子树为红色。

在这里插入图片描述对于这种情况,将bro右子树的颜色变为黑色,将bro结点颜色变为parent结点的颜色,将parent结点颜色改为黑色。但后对parent为根节点的子树左旋。在这样的子树内部调整完后,使得红黑树重新回复平衡,不需再继续调整。(代码中体现为break,因为后序还有删除操作,目前为调整操作)。

在这里插入图片描述

至此,删除结点为其父节点的左子树的情况已经全部分析完,删除结点为其父节点的右子树的情况与其类似,就不再叙述。

红黑树删除代码

bool erase(const pair<K, V>& kv)
{
	//1.找到要删除的位置
	node* del = _root;
	node* delParent = nullptr;
	while (del)
	{
		if (del->_kv.first > kv.first)
		{
			delParent = del;
			del = del->_left;
		}
		else if (del->_kv.first < kv.first)
		{
			delParent = del;
			del = del->_right;
		}
		else
		{
			//找到了
			if (del->_left == nullptr) //只有一个子结点
			{
				if (del == _root)
				{
					_root = _root->_right;
					if (_root)
					{
						_root->_parent = nullptr;
						_root->_col = BLACK;
					}
					delete del;
					return true;
				}

				break;
			}
			else if (del->_right == nullptr)
			{
				if (del == _root)
				{
					_root = _root->_left;
					if (_root)
					{
						_root->_parent = nullptr;
						_root->_col = BLACK;
					}
					delete del;
					return true;
				}
				break;
			}
			else
			{
				//左右子树都不为空,那么就要找个替死鬼
				node* minRight = del->_right;
				node* minPrev = del;
				while (minRight->_left)
				{
					minPrev = minRight;
					minRight = minRight->_left;
				}
				del->_kv = minRight->_kv;

				del = minRight;
				delParent = del->_parent;

				break;
			}
		}
	}
	//没找到
	if (del == nullptr)
	{
		return false;
	}
	node* delPos = del;
	node* delP = del->_parent;
	//调整红黑树
	if (del->_col == BLACK)
	{
		if (del->_left)
		{
			del->_left->_col = BLACK;
		}
		else if (del->_right)
		{
			del->_right->_col = BLACK;
		}
		else //删除的是叶子结点
		{
			while (del != _root)
			{
				if (del == delParent->_left)
				{
					//情况1:bro结点是红色
					if (delParent && delParent->_right && delParent->_right->_col == RED)
					{
						delParent->_col = RED;
						delParent->_right->_col = BLACK;
						RotateL(delParent);
						delParent = del->_parent;
					}
					//情况2:bro黑色并且其左右子树都是黑色或者都是nullptr
					if (delParent && delParent->_right && delParent->_right->_col == BLACK && 		((delParent->_right->_left == nullptr || delParent->_right->_left->_col == BLACK)
&& (delParent->_right->_right == nullptr || delParent->_right->_right->_col == BLACK)))
			/*if (delParent && delParent->_right && delParent->_right->_col == BLACK && ((delParent->_right->_left == nullptr && delParent->_right->_right == nullptr)
|| (delParent->_right->_left->_col == BLACK && delParent->_right->_right->_col == BLACK)))*/
					{
						if (delParent->_col == RED)
						{
							delParent->_right->_col = RED;
							delParent->_col = BLACK;
							break;
						}
						else
						{
							delParent->_right->_col = RED;
							del = delParent;
							delParent = del->_parent;
						}
					}
					else
					{
						//情况3:bro为黑色,并且其左孩子为红色,有孩子为空或黑色
						if (delParent && delParent->_right && delParent->_right->_col == BLACK &&
							delParent->_right->_left && delParent->_right->_left->_col == RED
		&& (delParent->_right->_right == nullptr || delParent->_right->_right->_col == BLACK))
						{
							delParent->_right->_left->_col = BLACK;
							delParent->_right->_col = RED;
							RotateR(delParent->_right);
						}
						//情况4:bro为黑色,并且其右孩子是红色的
						delParent->_right->_right->_col = BLACK;
						delParent->_right->_col = delParent->_col;
						delParent->_col = BLACK;
						RotateL(delParent);
						delParent = del->_parent;
						break;
					}
				}
				else
				{

					//情况1:bro结点是红色
					if (delParent && delParent->_left && delParent->_left->_col == RED)
					{
						delParent->_col = RED;
						delParent->_left->_col = BLACK;
						RotateR(delParent);
						delParent = del->_parent;
					}
					//情况2:bro黑色并且其左右子树都是黑色
					if (delParent && delParent->_left && delParent->_left->_col == BLACK && ((delParent->_left->_right == nullptr || delParent->_left->_right->_col == BLACK)
		&& (delParent->_left->_left == nullptr || delParent->_left->_left->_col == BLACK)))
					{
						if (delParent->_col == RED)
						{
							delParent->_left->_col = RED;
							delParent->_col = BLACK;
							break;
						}
						else
						{
							delParent->_left->_col = RED;
							del = delParent;
							delParent = del->_parent;
						}
					}
					else
					{
						//情况3:bro为黑色,并且其左孩子为红色,有孩子为空或黑色
						if (delParent && delParent->_left && delParent->_left->_col == BLACK &&
							delParent->_left->_right && delParent->_left->_right->_col == RED && (delParent->_left->_left == nullptr|| delParent->_left->_left->_col == BLACK))
						{
							delParent->_left->_right->_col = BLACK;
							delParent->_left->_col = RED;
							RotateL(delParent->_left);
						}
						//情况4:bro为黑色,并且其右孩子是红色的
						delParent->_left->_left->_col = BLACK;
						delParent->_left->_col = delParent->_col;
						delParent->_col = BLACK;
						RotateR(delParent);
						delParent = del->_parent;
						break;

					}
				}
			}
		}
	}

	//删除红黑树结点
	del = delPos;
	delParent = del->_parent;
	if (del->_left == nullptr)
	{
		if (del == delParent->_left)
		{
			delParent->_left = del->_right;
		}
		else
		{
			delParent->_right = del->_right;
		}
		if (del->_right)
		{
			del->_right->_parent = del->_parent;
		}
	}
	else
	{
		if (del == delParent->_left)
		{
			delParent->_left = del->_left;
		}
		else
		{
			delParent->_right = del->_left;
		}
		if (del->_left)
		{
			del->_left->_parent = del->_parent;
		}
	}
	delete del;
	return true;
}

代码分析:
在这里插入图片描述

截图中注释掉的代码是一个很坑的地方,情况2为bro为黑色,且其左右子树都为黑色,或者都是空树,注释的代码就是这个意思,注释掉的原因是因为,与下面的情况不兼容,由于情况2的代码是写在情况3和4上面的,所以如果调整的情况为3或者4,那么必先经过情况2的 if 判断,因为i情况3 4 中bro的左右节点中可能存在空的情况,在情况2的 if 判断时会出现空指针的解引用问题。所以,截图中未注释掉的代码巧妙的兼容了下面的3 4 情况。

四,红黑树与AVL树的比较

🚀红黑树与AVL树都是高效的二叉搜索树,它们的增删改查的时间复杂度都是O(logN),但是它们的区别是控制平衡的方式不同:
AVL树:采用平衡因子的方式(每个结点的平衡因子的绝对值不大于1),使得AVL是一颗高度平衡的二叉搜索树。
红黑树:采用颜色控制的方式,红黑树最长路径的结点个数不超过最短路径结点个数的2倍,所以红黑树是近似平衡的。

红黑树降低了在插入和删除时旋转的次数,所以在实际使用时红黑树的效率是更优的,并且红黑树的实现相对简单一些,所以红黑树的应用场景更广泛。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【OpenMMLab AI实战营二期笔记】第六天 MMDetection代码课

0. 环境检测和安装 # 安装 mmengine 和 mmcv 依赖 # 为了防止后续版本变更导致的代码无法运行&#xff0c;暂时锁死版本 pip install -U "openmim0.3.7" mim install "mmengine0.7.1" mim install "mmcv2.0.0"# Install mmdetection rm -rf mmd…

软考A计划-系统架构师-知识点汇总-上篇

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例点击跳转>软考全系列 &#x1f449;关于作者 专注于Android/Unity和各种游戏开发技巧&#xff…

Python转行的一点心得

Python现在非常火,语法简单而且功能强大&#xff0c;很多同学都想学Python&#xff01;最近陆陆续续有很多小伙伴问我&#xff0c;学Python到底应该做什么&#xff0c;从事哪种岗位。下面是我们工作圈里面一些同学的苦恼&#xff1a; 一转行要趁早 上面类似的问题群里还有很多…

华为HCIA备考 易错题整理

1.IEEE802.1Q定义的 VLAN 帧格式中VLAN ID总共有多少bit 答&#xff1a;12 2.NAPT允许多个私有IP地址通过不同的端口号映射到同一个公有IP地址上&#xff0c;且不需要做任何关于端口号的配置。 3.IEEE802.1Q定义的VLAN帧总长度为多少字节&#xff1f; 答&#xff1a;4 4.关于…

阿里云创建ALB_Ingress

阿里云参考文档 1、编写alb AlbConfig创建文档 rootbiking-pre-middleware:~/alb# cat ingress-alb.yaml apiVersion: alibabacloud.com/v1 kind: AlbConfig metadata: #alb名称name: ingress-alb spec:config:name: ingress-albaddressType: InternetzoneMappings:# 两个交换…

chatgpt赋能python:Python按行输出方法

Python按行输出方法 Python作为一种快速易用的编程语言&#xff0c;广泛应用于数据分析、科学计算、web开发等领域。在Python编程过程中&#xff0c;输出数据是必不可少的环节。本文将介绍如何按行输出Python数据&#xff0c;为Python编程初学者提供参考。 什么是按行输出 按…

consul入门案例及配置热更新的实现

Consul的简单入门 当Producer启动时,会向Consul发送一个post请求,告诉Consul自己的ip和Port;Consul接收到producer的注册后,每个10S(默认),会向producer发送一个健康检查的请求,检验Producer是否健康当Consumer发送GET方式请求/api/address到Producer时,会先从Consul中拿到一个…

超越密码:网络安全认证的未来

你的物理现实的数字对应物正在惊人地增长。虽然肯定会有积极的结果&#xff0c;但随着互联网的发展&#xff0c;与之相关的风险也在迅速增加。在讨论网络安全风险管理时&#xff0c;首先想到的是密码。但当出现诈骗、网络钓鱼等威胁时&#xff0c;这还不够。 那么&#xff0c;…

chatgpt赋能python:Python爬虫:抓取数据的实用技巧

Python爬虫&#xff1a;抓取数据的实用技巧 如果您是一名数字营销从业者&#xff0c;那么您一定知道SEO的重要性。SEO是一项复杂的工作&#xff0c;但是其中包含了一个非常关键的步骤&#xff0c;就是通过爬虫从网站中抓取数据。Python是一个非常强大的工具&#xff0c;可以帮…

网络层:静态路由配置及其可能产生的路由环路问题

网络层&#xff1a;静态路由配置及其可能产生的路由环路问题 笔记来源&#xff1a; 湖科大教书匠&#xff1a;静态路由配置及其可能产生的路由环路问题 声明&#xff1a;该学习笔记来自湖科大教书匠&#xff0c;笔记仅做学习参考 静态路由配置是指用户或网络管理员使用路由器的…

chatgpt赋能python:Python实现直线拟合及求斜率

Python实现直线拟合及求斜率 什么是直线拟合 直线拟合是一种数据处理方法&#xff0c;将一组数据点拟合成一条直线的形式&#xff0c;以求出其中的规律性关系&#xff0c;从而更好地理解数据点之间的相关性。 直线拟合的应用场景 直线拟合在很多领域都有应用&#xff0c;例…

openGauss5 企业版之开发设计规范

文章目录 1.数据库对象命名2. Database和Schema设计2.1 Database设计建议2.2 Schema设计建议 3. 表设计3.1选择存储方案3.2 选择分布方案3.3 选择分区方案3.4 选择分布键 1.数据库对象命名 数据库对象命名需要满足约束&#xff1a; 标识符非时序表长度不超过63个字节&#xff…

SSTI---总结

Laravel Blade是Laravel提供的一个既简单又强大的模板引擎 和其他流行的PHP模板引擎不一样&#xff0c;Blade并不限制你在视图view中使用原生的PHP代码 所有的Blade视图页面都将被编译成原生的PHP代码并缓存起来&#xff0c;除非你的的模板文件修改&#xff0c;否则不会重新编…

【RabbitMQ教程】第一章 —— RabbitMQ - 安装

&#x1f4a7; 【 R a b b i t M Q 教程】第一章—— R a b b i t M Q − 安装 \color{#FF1493}{【RabbitMQ教程】第一章 —— RabbitMQ - 安装} 【RabbitMQ教程】第一章——RabbitMQ−安装&#x1f4a7; &#x1f337; 仰望天空&#xff0c;妳我亦是行人.✨ &#x…

腾讯丁珂:以数字安全免疫力建设安全新范式

6月13日&#xff0c;腾讯安全联合IDC等多家机构在北京举办研讨论坛&#xff0c;并发布“数字安全免疫力”模型框架&#xff0c;提出用免疫的思维应对新时期下安全建设与企业发展难以协同的挑战。腾讯集团副总裁、腾讯安全总裁丁珂在论坛上表示&#xff0c;数智化新阶段&#xf…

springCloud 中,openFeign 使用说明

文章目录 1、openFeign 中的每个方法中的参数和注解不能少。2、开启日志打印功能3、超时 1、openFeign 中的每个方法中的参数和注解不能少。 如果服务端方法中的数据含有注解&#xff0c;则 客户端 openFeign 中的每个方法中的参数和注解一个不能少&#xff0c;比较完全一致。…

chatgpt赋能python:Python抠图教程:用代码实现高效抠图

Python 抠图教程&#xff1a;用代码实现高效抠图 什么是抠图&#xff1f; 在设计、美术、广告等领域中&#xff0c;经常需要把一张图片中的某个物体或人物单独提取出来&#xff0c;以便于进行后续的处理、叠加、合成等操作。这个过程就叫做抠图。 传统的抠图方式需要用到PS、…

【MCS-51】外接数码管

单片机可以连的显示外设有很多种&#xff0c;我们常用到的就是连接LED显示&#xff0c;但是除了LED以外&#xff0c;我们还有很多外部的显示元件&#xff0c;包括数码管、点阵屏等由圆管或者方管LED组成的显示屏&#xff0c;接下来我们着重来看如何使用51单片机外接数码管进行显…

chatgpt赋能python:Python如何打开Word文档?

Python 如何打开 Word 文档&#xff1f; Python 是一种强大的编程语言&#xff0c;可以帮助我们完成各种重复性工作&#xff0c;其中包括自动化文件的处理。在这篇文章中&#xff0c;我们将学习如何使用 Python 打开 Word 文档。本文将介绍三种不同的方式&#xff1a;使用 Pyt…

05-修建数据殿堂:Golang struct的艺术架构

&#x1f4c3;个人主页&#xff1a;个人主页 &#x1f525;系列专栏&#xff1a;Golang基础 &#x1f4ac;Go&#xff08;又称Golang&#xff09;是由Google开发的开源编程语言。它结合了静态类型的安全性和动态语言的灵活性&#xff0c;拥有高效的并发编程能力和简洁的语法。G…