【C++】---红黑树详解

news2024/11/15 10:03:59

【C++】---红黑树详解

  • 一、什么是红黑树?
    • 1、概念
    • 2、性质
    • 3、四个规则
  • 二、红黑树的定义
    • 1、红黑树 结点 定义
      • (1)将新插入的结点 设置为黑色
      • (2)将新插入的结点 设置为红色
    • 2、红黑树的定义
  • 三、红黑树插入
    • 1、插入节点
    • 2、控制颜色
      • (1)父亲为黑色
      • (2)父亲为红色
  • 四、红黑树颜色处理
    • 1、cur红、p红、g黑、u存在且为红
      • (1)抽象图
      • (2)具象图
    • 2、cur红、p红、g黑、u不存在 或者(u存在且为黑)
      • (1)单旋
        • ①抽象图:
        • ②具象图:
      • (2)双旋
        • ①抽象图:
        • ②具象图:
    • 3、红黑树 结点 的插入代码
  • 五、红黑树查找
  • 六、检查红黑树是否平衡
  • 七、红黑树的遍历
  • 八、红黑树的验证
  • 九、对Insert插入 总结、红黑树的注意事项:
    • (1)插入 总结
    • (2)注意事项

一、什么是红黑树?

1、概念

红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black。

2、性质

最长路径不超过最短路径的二倍
(路径:就是从 根节点 开始到 叶子节点 结束为止,叫做一条路径!)
在这里插入图片描述

3、四个规则

  1. 每个结点不是红色就是黑色
  2. 根节点是黑色的
  3. 红色节点不能连续!
  4. 红黑树的每条路径上的黑色节点数量必须相同!

只要满足以上4个规则,就一定符合,红黑树最长路径不超过最短路径的二倍。

但是反过来就不一定:

在这里插入图片描述

二、红黑树的定义

1、红黑树 结点 定义

红黑树相比于AVL树多了一个颜色,因此我们需要一个成员变量来储存颜色,而AVL树是一种严格 控制高度平衡 的二叉搜索树,但是红黑树是一种近似平衡的二叉搜索树,所以不需要平衡因子。

所以说可以这样区分和理解:
AVL树 :它需要平衡因子!
红黑树:不需要平衡因子,但是红黑树需要一个成员变量来储存颜色!

但是目前有一个问题,因为构造函数的时候红黑树 需要设置颜色,那么我们将新插入的节点设置为红色还是黑色呢?
(这样可以回归到我们的4个规则,主要就是维持规则3和规则4)

(1)将新插入的结点 设置为黑色

假如将新节点初始化成黑色,不管父亲是黑色还是红色,一定会破坏规则(4),并且影响其他路径,影响范围广。

①当父亲是黑色时,破坏了规则(4)
在这里插入图片描述
②当父亲是红色时,也破坏了规则(4)

在这里插入图片描述
所以总之如果将新插入的节点设置为黑色,你无论怎么插入它一定会破坏规则4。

(2)将新插入的结点 设置为红色

假如将新插入节点置为红色,会有以下两种情况:

①当父亲是黑色时,没有破坏规则(3),也没有破坏规则(4)

在这里插入图片描述

②当父亲是红色时,破坏了规则(3),但是只需要改变一下颜色即可

虽然破坏了规则三,但是规则三这个规则可以很容易就维护了你规则四如果破坏了,你需要每条路径都要改变,每条路径都受到影响了,根本维护不过来。
在这里插入图片描述
因此节点要初始化成红色。

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) // 将新插入的节点 初始化为 红色!
	{

	}
};

2、红黑树的定义

template<class K,class V>
class RBTree
{
	typedef RBTreeNode<K, V> Node;
 
    //构造函数
    RBTree()
		:_root(nullptr)
	{}
 
private:
	Node* _root;
};

三、红黑树插入

1、插入节点

插入节点分为2步:

(1) 先查找,如果树中已存在该节点,则插入失败

(2)树中不存在该节点,插入

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->_left;
				}
				else if(kv.first> cur->_kv.first)
				{
					parent = cur;
					cur = cur->_right;
				}
				else
				{
					return false; // 不允许重复 相同的直接false
				}
			}
			cur = new Node(kv);
			cur->_col = RED;// 默认:新插入的结点 为红色节点!
			
			if (kv.first < parent->_kv.first)
			{
				parent->_left = cur;
			}
			else
			{
				parent->_right = cur;
			}
			cur->_parent = parent;

// 此时已经算是插入上了,如果cur的parent是黑色就此结束,但是红色就需要调整了!

2、控制颜色

所以在插入节点之后,为了满足红黑树的性质,还需要调整节点颜色:

(1)父亲为黑色

如果父亲是黑色,那么不需要调整,4个规则都满足,插入已完成

在这里插入图片描述

(2)父亲为红色

如果父亲是红色,违反了规则(3),需要调整颜色
在这里插入图片描述

四、红黑树颜色处理

首先需要明白一点就是:什么时候要对红黑树的颜色进行处理?

那肯定是新插入节点是红色,这是已经提前设置好的,肯定是违反了规则三,新插入节点

cur,他的父亲肯定是红色,所以才违反了规则三。那既然父亲是红色节点,那他必然有爷

爷,因为红色节点不能当作根节点,所以说cur的爷爷必然是存在的,并且一定是黑色节

点,所以说一个红黑树这三个节点已经确定了!

①cur是红色
②父亲(parent)是红色
③爷爷(grandfather)是黑色
只有叔叔不确定,叔叔就是爷爷的左子树或者右子树,所以说变量就是叔叔,他是什么颜色
或者存在不存在,我们就以叔叔(uncle)这个变量来进行分类讨论!

1、cur红、p红、g黑、u存在且为红

当cur为红,p为红,g为黑,u存在且为红时,为了满足红黑树的性质,处理方法:变色
将p和u变黑,g变红

(1)抽象图

如下a、b、c、d、e都是子树:
在这里插入图片描述
(1)假如g是根节点,根据红黑树性质,根节点必须是黑色,那就把g再变黑就好了
在这里插入图片描述
(2)假如g不是根节点,g是子树,那么g还有parent节点。

①如果g的parent是黑色,满足红黑树性质,那么停止调整。

在这里插入图片描述
②如果g的parent是红色,就破坏了规则(3),那么还需要继续向上调整。
在这里插入图片描述
调整方法为:把g当作cur,继续向上调整,直到p不存在,或p存在且为黑停止。

(2)具象图

①g是根节点,直接把p和u变黑,g变红
在这里插入图片描述
②g不是根节点,g是子树,把p和u变黑,g变红之后,还要继续向上调整,直到p不存在,或p存在且为黑停止
在这里插入图片描述

2、cur红、p红、g黑、u不存在 或者(u存在且为黑)

这种情况下,g、p、cur形成直线,先看cur为红,p为红,g为黑,u为黑的情况

这是由情况一cur红,p红,g黑,u存在且为红处理以后变换而来,比如以下情况:

在这里插入图片描述
在这种情况下,cur原来的颜色一定是黑色,只不过在处理的过程当中,将cur的颜色变成了红色,所以cur不是新增节点,而是新增节点的g节点。

他这种(cur红、p红、g黑、u不存在 或者(u存在且为黑) )一定是有2层,x>=2。
解释如下:
在这里插入图片描述

(1)单旋

①抽象图:

如下a、b、c、d、e都是子树,由于要旋转,所以要分为两种情况:当p是g的左子树,cur是p的左子树时,g右单旋,p变黑,g变红:
在这里插入图片描述
当p是g的右子树,cur是p的右子树时,g左单旋,p变黑,g变红:

在这里插入图片描述

②具象图:

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

在这里插入图片描述

(2)双旋

双旋也就是 在反方向!

这种情况下g、p、cur形成折线,先看cur为红,p为红,g为黑,u为黑的情况:

①抽象图:

当p是g的左子树,cur是p的右子树时,处理方法:p左单旋,g右单旋,cur变黑,g变红:
在这里插入图片描述
当p是g的右子树,cur是p的左子树时,处理方法:p右单旋,就变成了情况二(单纯的一边高)
在这里插入图片描述

②具象图:

当p是g的左子树,cur是p的右子树时,将p左单旋,g右单旋,cur变黑,g变红:
在这里插入图片描述
当p是g的右子树,cur是p的左子树,p右单旋,g左单旋,p变黑,g变红
在这里插入图片描述
再看cur为红,p为红,g为黑,u不存在的情况,u不存在的情况更为简单:

当p是g的左子树,cur是p的右子树时, 将p左单旋,g右单旋,cur变黑,g变红

在这里插入图片描述
当p是g的右子树,cur是p的左子树,p右单旋就变成了情况二:
在这里插入图片描述

3、红黑树 结点 的插入代码

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->_left;
				}
				else if(kv.first> cur->_kv.first)
				{
					parent = cur;
					cur = cur->_right;
				}
				else
				{
					return false;
				}
			}
			cur = new Node(kv);
			cur->_col = RED;// 默认:新插入的结点 为红色节点!
			
			if (kv.first < parent->_kv.first)
			{
				parent->_left = cur;
			}
			else
			{
				parent->_right = cur;
			}
			cur->_parent = parent;

			// 此时已经算是插入上了,如果cur的parent是黑色就此结束,但是红色就需要调整了!

			while (parent && parent->_col == RED)
				// 这里的 parent && parent->_col == RED 中的 第1个parent就是来判断g是不是root
			{
				Node* grandfather = parent->_parent;

				// 1、变量uncle在grandfather的右边
				if (parent == grandfather->_left)
				{
					//(1)uncle存在&&为红   -------------// 变色 
					Node* uncle = grandfather->_right;

					if (uncle && uncle->_col == RED)
					{
						parent->_col = uncle->_col = BLACK;
						grandfather->_col = RED;

						//还要判断 此时的grandfather是不是root
						// ①g == root只需要让 g再次变黑即可!
						// ②g != root  把g赋值给cur 继续往上更新!
						cur = grandfather;
						parent = cur->_parent;

					}
					// (2)uncle不存在 || uncle存在 && 为黑色    ----// 变色 + 旋转
					else 
					{
						/*parent->_col = BLACK;
						grandfather->_col = RED;*/

						// 旋转:单旋 、双旋
						if (cur == parent->_left)
						{
							// (1)单旋
							//     g
							//   p   u
							// c

							 先变色 再旋转!

							//parent->_col = BLACK;
							//grandfather->_col = RED;
							//RS_rotation(grandfather);
							

							// 先旋转 再变色!
							RS_rotation(grandfather);
							parent->_col = BLACK;
							grandfather->_col = RED;
						}
						else 
						{
							// (1)双旋
							//       g
							//   p      u
							//     c

							 先变色 再旋转!
							//cur->_col = BLACK;
							//grandfather->_col = RED;
							//LS_rotation(parent);
							//RS_rotation(grandfather);

							// 先旋转 再变色!
							LS_rotation(parent);
							RS_rotation(grandfather);
							cur->_col = BLACK;
							grandfather->_col = RED;

						}

						break;// 因为else这种情况 变色:没有破坏规则  旋转:维持了红黑树的性质 所以完成之后可以直接 退出循环! 
					}
				}
				else
					// 2、变量uncle在grandfather的左边
				{
					Node* uncle = grandfather->_left;

					// (1)
					if (uncle && uncle->_col == RED)
					{
						parent->_col = uncle->_col = BLACK;
						grandfather->_col = RED;

						cur = grandfather;
						parent = cur->_parent;
					}
					else // (2)
					{

						if (cur == parent->_right)
						{

							
							//parent->_col = BLACK;
							//grandfather->_col = RED;
							//LS_rotation(grandfather);
							
							// 先旋转 再变色!
							LS_rotation(grandfather);
							parent->_col = BLACK;
							grandfather->_col = RED;
						}
						else
						{

							
							//parent->_col = BLACK;
							//grandfather->_col = RED;
							//RS_rotation(parent);
							//LS_rotation(grandfather);


							// 先旋转 再变色!
							RS_rotation(parent);
							LS_rotation(grandfather);
							cur->_col = BLACK;
							grandfather->_col = RED;
						}
						break;// 因为else这种情况 变色:没有破坏规则  旋转:维持了红黑树的性质 所以完成之后可以直接 退出循环! 
					}
					
				}
			}
			_root->_col = BLACK;
			return true;
		
	}

红黑树 先旋转再变色 or 先变色再旋转 都一样!!!

五、红黑树查找

	//查找
	Node* Find(const K& key)
	{
		Node* cur = _root;
		while (cur)
		{
			if (cur->_kv.first < key)//key比当前节点小,就向右查找
			{
				cur = cur->_right;
			}
			else if (cur->_kv.first > key)//key比当前节点大,就向左查找
			{
				cur = cur->_left;
			}
			else//找到了
			{
				return cur;
			}
		}
		return nullptr;//空树,直接返回
	}

六、检查红黑树是否平衡

检查是否平衡还是要用到递归子函数

(1)需要判断是否满足红黑树的4条性质

(2)计算 最左 / 最右 路径上的黑色节点个数,把它当做参考值:refNum!
递归路径时,需要计算每个路径上的黑色节点个数(blackNum)是否和最左路径上的节点个数相等

public:
bool Is_balance()// 主要就是 判断 这个树 符不符合 那4个规则!
	{
		if (_root->_col == RED)
		{
			return false;// 不符合 规则2 :根节点必须是黑色!
		}
		
		// 创建 一个 refNum 当做参照物 用来检验是否和blackNum相等 从而得出结论:blackNum思路正确 && 整个树符合规则4、
		// refNum 可以是 最左侧/最右侧!

		// int refNum = 1;  因为根节点是黑色 为啥初始化的时候 不直接给1?而是前置++
		int refNum = 0;
		Node* cur = _root;
		while (cur)
		{
			if (cur->_col == BLACK)
			{
				// refNum++;   因为根节点是黑色 所以要用前置++
				++refNum;
			}
			cur = cur->_left;
		}


		return _Check(_root, 0,refNum);
	}
private:
// 创建一个 blackNum 用来标记 每条路径下 黑色节点的数目!(递归形参!)
	bool _Check(Node* root,int blackNum,const int refNum)
	{
		if (root == nullptr)
		{
			if (refNum != blackNum)
			{
				cout << "存在黑色结点不相等的 路径" << endl;
				return false;
			}

			return true;
		}

		if (root->_col == RED && root->_parent->_col == RED)
		{
			cout <<root->_kv.first <<  "存在连续的红色结点!" << endl;
			return false;// 不符合规则3: 不能存在连续的红色节点!
		}


		if (root->_col == BLACK)
		{
			blackNum++;
		}

		// 递归遍历整个树!
		return _Check(root->_left, blackNum, refNum) &&
			   _Check(root->_right, blackNum, refNum);
	}

七、红黑树的遍历

	//遍历
	void _InOrder(Node* root)
	{
		if (root == nullptr)
		{
			return;
		}
 
		_InOrder(root->_left);
		cout << root->_kv.first << ":" << root->_kv.second << endl;
		_InOrder(root->_right);
	}
 
	void InOrder()
	{
		_InOrder(_root);
		cout << endl;
	}

八、红黑树的验证

void TestRBTree1()
{
	int a[] = { 4,2,6,1,3,5,15,7,16,14,8,3,1,10,6,4,7,14,13 };

	RBTree<int, int> t1;
	for (auto e : a)
	{
		if (e == 10)
		{
			int i = 0;
		}
		t1.Insert({ e,e });

		cout << "Insert:" << e << "->" << t1.Is_balance() << endl;
	}

	t1.InOrder();

	cout << t1.Is_balance() << endl;
}

在这里插入图片描述

九、对Insert插入 总结、红黑树的注意事项:

(1)插入 总结

三种情况:
(1)uncle存在且为red
(2)uncle存在且为black
(3)uncle不存在

实际上,情况二 和 情况三 可以合并 为同一种情况!

在这里插入图片描述

(2)注意事项

1、对参考值 refNum 的初始化 refNum=0 、refNum必须是前置++
在这里插入图片描述

2、为啥要将 refNum 和 blackNum 判断是否相等 放在 if(root是否等于nullptr)里面?

在这里插入图片描述

3、双旋的注意事项:
在这里插入图片描述


好了,今天的分享就到这里了
如果对你有帮助,记得点赞👍+关注哦!
我的主页还有其他文章,欢迎学习指点。关注我,让我们一起学习,一起成长吧!
在这里插入图片描述

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

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

相关文章

Zabbix自动导出PDF报告

zabbix6提供了定时导出PDF报告功能。此功能可按照Dashboard维度&#xff0c;定时自动导出报告&#xff0c;并通过邮件发送。 1.安装 zabbix 提供了官方的rhel8版本的rpm包&#xff0c;可使用yum方式安装&#xff0c;zabbix自动导出PDF功能是基于go环境的zabbix web service程…

应用方案 | 低功耗接地故障控制器D4145

一、概述 D4145 是一个接地故障断路器。它能够检测到不良的接地条件&#xff0c;譬如装置接触到水时&#xff0c;它会在有害或致命的电击发生之前将电路断开。 D4145能检测并保护从火线到地线,从零线到地线的故障.这种简单而传统的电路设计能够确保其应用自如和长时间的可靠性。…

Vue3+Echarts+Setup实现动态曲线堆叠图+图例分页

提前安装引入echarts 效果图 dom实例 <div id"rightCharterwang" style"height: 28vh"></div> 配置项&#xff0c;将数据换成从接口请求回来的数据&#xff08;这里是写死的假数据&#xff09; const rightCharterwang () > {let named…

vcruntime140_1.dll丢失是什样的错误?五种vcruntime140_1.dll修复方法详细步骤教程

对于经常使用Windows操作系统的用户来说&#xff0c;面对“vcruntime140_1.dll文件丢失”的错误提示可能既熟悉又令人苦恼。这个错误通常发生在尝试启动或安装一些依赖于此特定DLL文件的应用程序时&#xff0c;在本文中&#xff0c;我们将详细介绍 ​vcruntime140_1.dll​ 所承…

使用Python和Pillow创建照片马赛克应用

在这篇博客中,我们将探讨如何使用Python创建一个简单而有趣的桌面应用程序。我们的目标是构建一个应用,允许用户选择一张照片,然后在照片的右下角添加马赛克效果。这个项目将展示如何结合使用wxPython来创建图形用户界面(GUI)和Pillow库来处理图像。 D:\spiderdocs\eraselogo.p…

Linux 基本指令讲解 上

linux 基本指令 clear 清屏 Alt Enter 全屏/退出全屏 pwd 显示当前用户所处路径 cd 改变目录 cd /root/mikecd … 返回上级目录cd - 返回最近所处的路径cd ~ 直接返回当前用户自己的家目 roor 中&#xff1a;/root普通用户中&#xff1a;/home/mike mkdir 创建一个文件夹(d) …

通义灵码:AI 研发趋势与效果提升实践丨SDCon 全球软件技术大会演讲全文整理

作者&#xff1a;张昕东 大家好&#xff0c;我是来自阿里云通义灵码团队的张昕东。很高兴和各位同仁做这次分享&#xff0c;分享的主题是人机协同趋势与效果提升实践。我们所做的模型提升和功能开发是为了促进人机在研发领域的协同&#xff0c;而当今的人机协同现状又决定了我…

基于Spring Boot的农田智能管理系统

目录 前言 功能设计 系统实现 获取源码 博主主页&#xff1a;百成Java 往期系列&#xff1a;Spring Boot、SSM、JavaWeb、python、小程序 前言 农田智能管理系统是基于SpringBoot框架开发的一款针对农田管理的智能化平台。随着农业现代化的发展&#xff0c;农田管理需要更…

docker拉取kafka镜像|启动kafka容器

1、kafka官网快速开始模块查看如何拉取kafka的docker镜像 https://kafka.apache.org/quickstart 2、移除本机已拉取kafka的docker镜像 docker rmi apache/kafka:3.7.03、拉取kafka的docker镜像 docker pull apache/kafka:3.7.04、启动kafka容器 docker run -p 9092:9092 ap…

iOS 18(macOS 15)Vision 中新增的任意图片智能评分功能试玩

概述 在 WWDC 24 中库克“大厨”除了为 iOS 18 等平台重磅新增了 Apple Intelligence 以外&#xff0c;苹果也利用愈发成熟的机器学习引擎扩展了诸多内置框架&#xff0c;其中就包括 Vision。 想用本机人工智能自动为我们心仪的图片打一个“观赏分”吗&#xff1f;“如意如意&…

【2.3】回溯算法-重新排序得到 2 的幂

一、题目 给定正整数N&#xff0c;我们按任何顺序&#xff08;包括原始顺序&#xff09;将 数字重新排序 &#xff0c;注意其前导数字不能为零。 如果我们可以通过上述方式得到2的幂&#xff0c;返回 true&#xff1b;否则&#xff0c;返回false。 提示&#xff1a; 1 < …

学习记录第二十六天

进程运行 1&#xff0c;子进程和父进程做相同的事----创建子进程 执行任务 2&#xff0c;子进程做与父进程不同的事 ----fork exec exec族 l VS v :主要是第二个参数的传参方式不同 p :表示寻找可执行文件 是通过PATA环境变量 e : 表示可以给…

升级软文发稿开源系统源码论文期刊一键发布

升级软文发稿运营管理源码—论文期刊一键发布 软文发稿系统源码&#xff08;软文发布系统&#xff09;在基于旧版本的媒介软文发布平台项目改造升级了新的功能模块简称&#xff08;3.0版&#xff09;本系统还是基于开源的PHPMYSQLlayui&#xff08;前端界面&#xff09;代码进行…

唐山网站建设方案优化

唐山作为一个重要的工业城市&#xff0c;网站建设在这里具有重要的意义。为了更好地服务于市民和企业&#xff0c;唐山网站建设方案需要不断优化和更新。下面将从内容、技术和设计三个方面来探讨唐山网站建设方案的优化。 首先是内容方面。唐山作为一个历史悠久且拥有丰富文化底…

如何提高遭受网络攻击后的恢复速度

现实促使组织探索提供更快恢复和增强安全性的替代网络保护和恢复方法&#xff0c;尤其是在更严格的法规驱动下&#xff0c;这种紧迫感愈加明显。以下是一些提高网络安全策略恢复速度的方法。 近年来&#xff0c;勒索软件攻击已成为一个重大且令人担忧的趋势&#xff0c;其频率和…

Spring之最基础的创建与Bean的存储与获取(还有Maven加载jar包报错的解决方法)

目录 创建Spring项目 如何解决Maven加载jar包报错 存储Bean和取Bean 在Java中对象也称为Bean。所以后面我提到Bean&#xff0c;大家要知道我说的是什么。 创建Spring项目 我的idea是2022版本的&#xff0c;如果大家和我一样可以参考我的。 1.创建一个Maven项目。图片上忘了…

【TiDB】08-离线部署TiDB 8.1

目录 1、环境检查 1.1、检测及关闭系统 swap 1.2、设置TiDB节点的临时空间 1.3、安装NTP服务 1.3.1、更新apt源 1.3.2、安装NTP 1.3.3、将本机作为NTP服务器 1.3.4、客户端连接 1.4、配置SSH互信及sudo免密码 2、离线安装 2.1、下载离线安装包 2.2、解压安装 2.3、…

YOLOv8改进 | 主干网络 | 动态调整目标的感受野的LSKNet【旋转目标检测SOTA】

秋招面试专栏推荐 &#xff1a;深度学习算法工程师面试问题总结【百面算法工程师】——点击即可跳转 &#x1f4a1;&#x1f4a1;&#x1f4a1;本专栏所有程序均经过测试&#xff0c;可成功执行&#x1f4a1;&#x1f4a1;&#x1f4a1; 专栏目录 &#xff1a;《YOLOv8改进有效…

程序员最常用的操作——git

软件安装 官方下载&#xff08;比较慢&#xff09;&#xff1a;https://git-scm.com/downloads gitee 注册账号 https://gitee.com/ 新建仓库 honey2024 先全局配置下邮箱 配置 git config --global user.email “your_emailexample.com” git config --global user.name…

虚幻5|AI行为树,进阶篇

一&#xff0c;打开敌人的角色蓝图&#xff0c;编写以下蓝图&#xff0c;该蓝图只是创建一个敌人并非ai行为树 1.编写蓝图 2.打开主界面&#xff0c;创建一个导航网格体积&#xff0c;上一章都有讲&#xff0c;在添加体积这里面&#xff0c;找到导航网格体积&#xff0c;点击创…