红黑树介绍与模拟实现(insert+颜色调整精美图示超详解哦)

news2025/1/19 22:20:40

红黑树

  • 引言
  • 红黑树的介绍
  • 实现
    • 结点类
    • insert
      • 搜索插入位置
      • 插入
      • 调整
        • 当parent为gparent的左子结点
        • 当parent为gparent的右子结点
  • 参考源码
  • 测试红黑树是否合格
  • 总结

引言

在上一篇文章中我们认识了高度平衡的平衡二叉树AVL树:戳我看AVL树详解哦
(关于旋转调整的部分,在AVL树的时候已经详细介绍过了,如果大家对旋转调平衡的部分有疑惑的话,请移步至AVL树的详解)

由于AVL树的高度平衡,其平均搜索时间复杂度几乎可以达到严格的O(logN)。同样也因为平衡的程度很高,在维护平衡上时间的花费对于搜索上时间的提升是得不偿失的。
大多数情况下,我们并不需要很高的平衡程度,只需要达成一种接近平衡的状态,搜索的平均时间复杂度基本达到O(logN)即可。 红黑树就是这样的一种结构,它的最高子树的高度小于等于最低子树的二倍。通过减少调平衡时的时间成本来提高效率

红黑树的介绍

红黑树是平衡二叉树的一种,他在满足二叉搜索树特性的基础上,给每个结点增加了一个颜色属性,包括RedBlack;并要求从根节点到一个叶子结点形成的任意一条路径中,通过对结点颜色的限制规则,没有任何一条路径回比其他的路径长一倍
在这里插入图片描述
(红黑圣诞树)

对于红黑树结点颜色的限制规则如下:

  • 每个结点的颜色只有红色或黑色两种
  • 根结点的颜色一定是黑色的
  • 如果某一个结点的颜色是红色的,它的两个孩子结点的颜色一定是黑色(即不存在两个连续的红色结点);
  • 对于任一结点,到叶子结点的任一路径上,包含的黑色结点的数量一定相等
  • 叶子结点一定是黑色的(这里的叶子结点直最后的nullptr

当满足上面的所有规则时,根节点到叶子结点的任一路径就都不可能比其他路径长一倍。由于不存在连续的红色结点,所以当黑色结点的数量 n 一定时,最长路径的长度为2 * n,最短路径的长度为 n ,所以不可能相差一倍以上。这样就达成了一种相对平衡的状态,并不需要经常去旋转调平了。

实现

红黑树的实现,在之前的二叉搜索树上增加了结点的颜色,以及对于结点的颜色调整的部分;
在本篇文章中依旧实现 K-V的模式 的树, 并且以非递归实现insert
为防止命名冲突,将实现放在我们的命名空间qqq中:

结点的颜色我们使用 枚举常量enum Color 来表示;

RBTree是一个类模板,有两个模板参数,即KV,表示其中存储的索引类型与值类型;
成员变量类型为Node*(由结点类RBTreeNode重命名),表示根结点的指针:

 //基本的代码结构
namespace qqq
{
	enum Color //枚举常量表示颜色
	{
		RED,
		BLUCK
	};

	template<class K, class V>
	struct RBTreeNode  //结点类
	{};

	template<class K, class V>
	class RBTree  //红黑树
	{
		typedef RBTreeNode<K, V> Node;
	public:
		bool insert(const pair<K, V>& kv)
		{}
	protected:
		Node* _root = nullptr;
	};
}

结点类

首先,对红黑树的结点进行实现:

RBTreeNode是也一个类模板, 两个模板参数同样为KV,表示索引与值的类型;
在结点中储存数据的结构为pair,其中firstK类型,secondV类型;

成员变量包括结点中的数据 _kv
指向父亲结点的指针 _parent
指向左右孩子结点的指针 _left_right
表示结点颜色的枚举常量 _col

template<class K, class V>
struct RBTreeNode  //结点类
{
	pair<K, V> _kv;
	RBTreeNode<K, V>* _parent;
	RBTreeNode<K, V>* _left;
	RBTreeNode<K, V>* _right;
	Color _col;
};

结点类的构造函数
我们需要实现一个默认构造函数:
参数类型为const pair<K, V>,缺省值为一个pair<K, V>的匿名对象;
对于父子结点的指针,在初始化列表中初始化为nullptr即可;
对于结点的颜色,在初始化列表中初始化为RED(要保证每条路上黑色结点的个数相等,就必须初始化为红色):

	RBTreeNode(const pair<K, V> kv = pair<K, V>())
		: _kv(kv)
		, _left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _col(RED)
	{}

insert

红黑树的insert分为两个部分,即搜索并插入以及调整使其满足红黑树的性质:

搜索插入位置

  • 与二叉搜索树类似,搜索并插入时,首先用要插入的pair对象创建一个新结点newnode
  • 与此同时,使用结点指针 cur来记录当前位置parent来记录cur的父结点,便于后面插入;
  • 然后 while循环向下查找 插入的位置:当newnode小于cur的元素时,向左查找,当newnode大于cur的元素时,向右查找,相等时即该元素已经存在,返回false
  • curnullptr时,表示找到了插入的位置,循环终止:
// 部分代码:搜索插入位置 //
Node* newnode = new Node(kv);
Node* parent = nullptr;
Node* cur = _root;

while (cur != nullptr) //搜索
{
	if (newnode->_kv.first > cur->_kv.first)
	{
		parent = cur;
		cur = parent->_right;
	}
	else if (newnode->_kv.first < cur->_kv.first)
	{
		parent = cur;
		cur = parent->_left;
	}
	else  //相等即插入失败
	{
		return false;
	}
}

插入

  • newnode插入红黑树即将newnodeparent链接,这时就需要判断parent是否为空
  • parent为空时,即cur就是根节点_root,即newnode是这棵红黑树中的第一个结点,将 其赋值给_root 即可;
  • parent不为空时, 还需要判断cur位于parent的左边还是右边,然后再插入:

在这里插入图片描述

//  局部代码:插入newnode //
if (parent == nullptr) //插入
{
	_root = newnode;
}
else if (newnode->_kv.first < parent->_kv.first)
{
	parent->_left = newnode;
	newnode->_parent = parent;
	cur = newnode;
}
else
{
	parent->_right = newnode;
	newnode->_parent = parent;
	cur = newnode;
}

调整

在完成插入后,首先需要判断的是parent指针的状态:

  • parent为空时,表明当前结点为根结点,将其颜色改为BLACK即可调整完毕;
  • parent指向结点的颜色为BLACK 时,如果是在刚插入时,新插入的结点颜色为RED,不会影响该路径的黑色节点个数,所以不再需要调整。如果是在向上调整的过程中parent指向的结点为BLACK,也意味着整棵树调整结束了(这一点在后面的调整部分会详细介绍);
  • 除了上面不用调整的两种情况外,其余的情况,即 parent指向的结点存在且为RED 的情况就需要进行调整了。调整是自下而上的,循环向上调整,直到 parent为空或parent指向的结点为BLACK时循环结束,调整完成
// 部分代码:自下而上调整的循环框架 //
while (parent != nullptr && parent->_col != BLUCK) 
{}

开始调整时首先对parentgparentcur的祖父结点)的左子结点还是右子结点做一分类讨论(当parent不为黑时,由于根结点必须为黑,所以parent不是根结点,所以gparent一定存在):

当parent为gparent的左子结点

parentgparent的左子结点时,我们首先要考察cur叔叔结点的情况:

  • cur的叔叔结点为RED 时,不需要进行旋转调整,只需要将gparent指向结点设置为RED,将parentcur的叔叔结点全部设置为BLACK即可:
    由于在调整完颜色后,gparent指向结点颜色就为RED了,这时如果gparent父结点的颜色正好为红,就出现了连续的两个红色结点。所以需要cur向上移动两个结点,再对其parent指向的结点继续进行判断:
    在这里插入图片描述

  • cur的叔叔结点为BLACK或不存在时,就需要进行旋转调整了:
    旋转的逻辑与之前AVL树类似,当cur位于parent的左边时,即左左——单次右旋
    在这里插入图片描述
    cur位于parent的右边时,即左右——左右双旋
    在这里插入图片描述
    在这里插入图片描述
    旋转调整后将gparent指向结点设置为RED,将子树顶部的结点设置为BLACK 即可(parent指向的结点或cur指向的结点):
    在这里插入图片描述

经过旋转后的红黑树,子树顶部的颜色一定为黑色,再向上也就不会存在两个连续的红色结点的问题了,所以旋转之后直接终止循环即可。

需要注意的是叔叔是黑色结点的情况一定是出现在调整过程中发生的,当叔叔结点为黑色时,cur下的路径中一定存在着与gparent右子树中黑色结点相同个数的黑色结点,parent下的路径中同样也存在着相同数目的黑色结点,这样在旋转调平衡后,这棵子树中的所有路径中的黑色结点数目与之前是不变的

//部分代码:当parent为gparent左子结点的情况
if (parent == gparent->_left)   
{
	if (gparent->_right == nullptr || gparent->_right->_col == BLUCK)  //1.叔叔结点不存在或为黑,需要旋转并调色
	{
		if (cur == parent->_left)//左左->单次右旋
		{
			RotateR(gparent);
			parent->_col = BLUCK;
			gparent->_col = RED;
		}
		else//左右->左旋+右旋
		{
			RotateL(parent);
			RotateR(gparent);

			cur->_col = BLUCK;
			gparent->_col = RED;
		}
		break; //通过旋转调整后,该子树的根结点一定是黑,所以可以直接结束循环
	}
	else if (gparent->_right->_col == RED)  //2.叔叔结点为红,通过调色即可实现红黑树
	{
		//调色
		parent->_col = BLUCK;
		gparent->_right->_col = BLUCK;
		gparent->_col = RED;
		//继续向上
		cur = gparent;
		parent = cur->_parent;
	}
	else
	{
		assert(0);
	}
}
当parent为gparent的右子结点

parentgparent的右子结点时,与上面的情况一致,只是左右对调了,所以这里只给出图示与代码(如果在这种情况下遇到了问题,希望你在上面的情况中能够找到答案):

  • 叔叔结点为RED,仅调整颜色
    在这里插入图片描述

  • 叔叔结点为BLACK或不存在,左单旋或右左双旋:
    左单旋
    在这里插入图片描述
    右左双旋
    在这里插入图片描述在这里插入图片描述
    旋转后调整颜色
    在这里插入图片描述

旋转后子树顶部的结点一定为BLACK,所以直接break即可。

//部分代码:当parent为gparent右子结点的情况 //
else //parent == gparent->_right
{
	if (gparent->_left == nullptr || gparent->_left->_col == BLUCK)  //1.叔叔结点不存在或为黑,需要旋转并调色
	{
		if (cur == parent->_right)//右右->单次左旋
		{
			RotateL(gparent);
			parent->_col = BLUCK;
			gparent->_col = RED;
		}
		else//右左->右旋+左旋
		{
			RotateR(parent);
			RotateL(gparent);

			cur->_col = BLUCK;
			gparent->_col = RED;
		}
		break; //通过旋转调整后,该子树的根结点一定是黑,所以可以直接结束循环
	}
	else if (gparent->_left->_col == RED)  //2.叔叔结点为红,通过调色即可实现红黑树
	{
		//调色
		parent->_col = BLUCK;
		gparent->_left->_col = BLUCK;
		gparent->_col = RED;
		//继续向上
		cur = gparent;
		parent = cur->_parent;
	}
	else
	{
		assert(0);
	}
}

while循环调整结束之后,再将根结点_root的颜色改为BLACK,统一做处理(insert的整体代码在这里就不做展示了,大家跳转至参考源码部分查看即可)。

参考源码

namespace qqq
{
	enum Color //枚举常量表示颜色
	{
		RED,
		BLUCK
	};

	template<class K, class V>
	struct RBTreeNode  //结点类
	{
		pair<K, V> _kv;
		RBTreeNode<K, V>* _parent;
		RBTreeNode<K, V>* _left;
		RBTreeNode<K, V>* _right;
		Color _col;

		RBTreeNode(const pair<K, V> kv = pair<K, V>())
			: _kv(kv)
			, _left(nullptr)
			, _right(nullptr)
			, _parent(nullptr)
			, _col(RED)
		{}
	};

	template<class K, class V>
	class RBTree  //红黑树
	{
		typedef RBTreeNode<K, V> Node;

	public:
		bool insert(const pair<K, V>& kv)
		{ 
			//先插入
			Node* newnode = new Node(kv);
			Node* parent = nullptr;
			Node* cur = _root;

			while (cur != nullptr) //搜索
			{
				if (newnode->_kv.first > cur->_kv.first)
				{
					parent = cur;
					cur = parent->_right;
				}
				else if (newnode->_kv.first < cur->_kv.first)
				{
					parent = cur;
					cur = parent->_left;
				}
				else  //相等即插入失败
				{
					return false;
				}
			}
			if (parent == nullptr) //插入
			{
				_root = newnode;
			}
			else if (newnode->_kv.first < parent->_kv.first)
			{
				parent->_left = newnode;
				newnode->_parent = parent;
				cur = newnode;
			}
			else
			{
				parent->_right = newnode;
				newnode->_parent = parent;
				cur = newnode;
			}

			//调整颜色以及旋转使满足红黑树
			while (parent != nullptr && parent->_col != BLUCK) //当parent不为黑时,由于根结点必须为黑,所以parent不是根结点,所以gparent一定存在
			{
				Node* gparent = parent->_parent;

				if (parent == gparent->_left)   
				{
					if (gparent->_right == nullptr || gparent->_right->_col == BLUCK)  //1.叔叔结点不存在或为黑,需要旋转并调色
					{
						if (cur == parent->_left)//左左->单次右旋
						{
							RotateR(gparent);
							parent->_col = BLUCK;
							gparent->_col = RED;
						}
						else//左右->左旋+右旋
						{
							RotateL(parent);
							RotateR(gparent);

							cur->_col = BLUCK;
							gparent->_col = RED;
						}
						break; //通过旋转调整后,该子树的根结点一定是黑,所以可以直接结束循环
					}
					else if (gparent->_right->_col == RED)  //2.叔叔结点为红,通过调色即可实现红黑树
					{
						//调色
						parent->_col = BLUCK;
						gparent->_right->_col = BLUCK;
						gparent->_col = RED;
						//继续向上
						cur = gparent;
						parent = cur->_parent;
					}
					else
					{
						assert(0);
					}
				}
				else
				{
					if (gparent->_left == nullptr || gparent->_left->_col == BLUCK)  //1.叔叔结点不存在或为黑,需要旋转并调色
					{
						if (cur == parent->_right)//右右->单次左旋
						{
							RotateL(gparent);
							parent->_col = BLUCK;
							gparent->_col = RED;
						}
						else//右左->右旋+左旋
						{
							RotateR(parent);
							RotateL(gparent);

							cur->_col = BLUCK;
							gparent->_col = RED;
						}
						break; //通过旋转调整后,该子树的根结点一定是黑,所以可以直接结束循环
					}
					else if (gparent->_left->_col == RED)  //2.叔叔结点为红,通过调色即可实现红黑树
					{
						//调色
						parent->_col = BLUCK;
						gparent->_left->_col = BLUCK;
						gparent->_col = RED;
						//继续向上
						cur = gparent;
						parent = cur->_parent;
					}
					else
					{
						assert(0);
					}
				}
			}
			
			_root->_col = BLUCK;
			return true;
		}
		
		void RotateL(Node* parent)
		{
			Node* gparent = parent->_parent;
			Node* cur = parent->_right;
			Node* curleft = cur->_left;

			parent->_right = curleft;
			if (curleft != nullptr)
			{
				curleft->_parent = parent;
			}

			cur->_left = parent;
			parent->_parent = cur;

			cur->_parent = gparent;
			if (gparent == nullptr)
			{
				_root = cur;
			}
			else
			{
				if (cur->_kv.first < gparent->_kv.first)
				{
					gparent->_left = cur;
				}
				else
				{
					gparent->_right = cur;
				}
			}
		}
		void RotateR(Node* parent)
		{
			Node* gparent = parent->_parent;
			Node* cur = parent->_left;
			Node* curright = cur->_right;

			parent->_left = curright;
			if (curright != nullptr)
			{
				curright->_parent = parent;
			}

			cur->_right = parent;
			parent->_parent = cur;

			cur->_parent = gparent;
			if (gparent == nullptr)
			{
				_root = cur;
			}
			else
			{
				if (cur->_kv.first < gparent->_kv.first)
				{
					gparent->_left = cur;
				}
				else
				{
					gparent->_right = cur;
				}
			}
		}

		bool isRBTree()
		{
			return isRBTree(_root);
		}

	protected:
		Node* _root = nullptr;

		bool checkColor(Node* root, int countBluck, int& baseBluck) //计算黑结点数量与红结点连续是否满足条件
		{
			if (root == nullptr)
			{
				if (baseBluck == countBluck)
				{
					return true;
				}
				return false;
			}

			if (root->_col == RED && root->_parent != nullptr && root->_parent->_col == RED)
			{
				return false;
			}

			if (root->_col == BLUCK)
			{
				countBluck++;
			}
			return checkColor(root->_left, countBluck, baseBluck) && checkColor(root->_right, countBluck, baseBluck);
		}

		bool isRBTree(Node* root)
		{
			if (root == nullptr)
				return true;
			if (root->_col == RED)
				return false;

			//先计算一条路径中的黑色结点数量
			int baseBluck = 0;
			Node* cur = root;
			while (cur != nullptr)
			{
				if (cur->_col == BLUCK)
				{
					++baseBluck;
				}
				cur = cur->_right;
			}

			return checkColor(root, 0, baseBluck);
		}
	};
}

测试红黑树是否合格

在写完红黑树的insert之后,我们可以再编写一个测试模块来测试一棵树是否满足红黑树的特性

我们其实只需要判断两点即可:

  • 任一路径上的黑色结点个数是否相等
  • 是否存在连续的两个红色结点

我们使用递归的方式来判断:

判断任一路径上的黑色结点个数是否相等时,必须要先计算某条路径上的黑色结点个数,可以通过 while循环计算最右侧一条路径上的黑色结点个数。将cur_root开始一直向右子节点遍历即可,当cur为空时即该路径结束,终止循环。

然后使用checkColor函数(参数为当前结点指针Node*,计算中的当前路径黑色结点个数int以及先前计算的最外层黑色结点个数int )递归计算每条路径上的黑色结点个数,顺便判断是否存在连续的红色结点。
在递归过程中:
_root为空时,即当前路径已经结束,判断countBluckbaseBlack的值是否相等,若相等返回true,否则返回false
若当前结点为红色,则判断其父结点是否为红色,是就返回false
若当前结点为黑色,countBluck加1,并继续向左右子结点递归,返回左子结点的结果&&右子结点的结果:
在这里插入图片描述

测试红黑树是否合格的代码这里就不赘述了,大家可以在参考源码部分查找。这里来展示一下测试结果

namespace qqq
{
	void testfunc()
	{
		const int N = 10000000;
		vector<int> v;
		v.reserve(N);
		srand(time(0));

		for (size_t i = 0; i < N; i++)
		{
			v.push_back(i);
		}

		RBTree<int, int> rbt;
		for (auto e : v)
		{
			rbt.insert(make_pair(e, e));
			cout << "insert:" << e << "->" << rbt.isRBTree() << endl;
		}
		cout << rbt.isRBTree() << endl;
	}
}

int main()
{
	qqq::testfunc();
	return 0;
}

在这里插入图片描述
因为这段测试代码中,存在大量I/O,所以运行速度很慢,大家可以将cout注释掉,只打印最后的结果。

总结

到此,关于红黑树的知识就介绍完了
在接下来的文章中,将会对红黑树进行封装,即mapset,尽情期待哦

如果大家认为我对某一部分没有介绍清楚或者某一部分出了问题,欢迎大家在评论区提出

如果本文对你有帮助,希望一键三连哦

希望与大家共同进步哦

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

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

相关文章

Sealos 一键部署FastGPT的解决方案

&#x1f9d9;‍♂️ 诸位好&#xff0c;吾乃斜杠君&#xff0c;编程界之翘楚&#xff0c;代码之大师。算法如流水&#xff0c;逻辑如棋局。 &#x1f4dc; 吾之笔记&#xff0c;内含诸般技术之秘诀。吾欲以此笔记&#xff0c;传授编程之道&#xff0c;助汝解技术难题。 &#…

1.数据结构和算法

文章目录 数据结构逻辑结构集合结构线性结构树形结构图形结构 物理结构顺序存储结构链式存储结构 算法基本特性目标 总结数据结构总结算法总结 数据结构 「数据结构」指的是&#xff1a;数据的组织结构&#xff0c;用来组织、存储数据。 逻辑结构 逻辑结构&#xff08;Logic…

解决oracle数据库乱码

解决oracle数据库乱码 [oraclep19cstd dbca]$ vim ~/.bash_profile #再文件末尾加上 export NLS_LANGAMERICAN_AMERICA.AL32UTF8[oraclep19cstd dbca]$ source ~/.bash_profile [oraclep19cstd dbca]$ sqlplus / as sysdba

Spring声明式事务以及事务传播行为

Spring声明式事务以及事务传播行为 Spring声明式事务1.编程式事务2.使用AOP改造编程式事务3.Spring声明式事务 事务传播行为 如果对数据库事务不太熟悉&#xff0c;可以阅读上一篇博客简单回顾一下&#xff1a;MySQL事务以及并发访问隔离级别 Spring声明式事务 事务一般添加到…

安装Docker(CentOS)

Docker 分为 CE 和 EE 两大版本。CE 即社区版&#xff08;免费&#xff0c;支持周期 7 个月&#xff09;&#xff0c;EE 即企业版&#xff0c;强调安全&#xff0c;付费使用&#xff0c;支持周期 24 个月。 Docker CE 分为 stable test 和 nightly 三个更新频道。 官方网站上…

212 基于matlab的双稳态随机共振的算法

基于matlab的双稳态随机共振的算法&#xff0c;分析信噪比随系统参数a,b及乘性噪声和加性噪声的增益变化曲线&#xff0c;60个数据样本可供选择。程序已调通&#xff0c;可直接运行。 212 双稳态随机共振 信噪比增益变化曲线 - 小红书 (xiaohongshu.com)

【SQL Server的详细使用教程】

&#x1f3a5;博主&#xff1a;程序员不想YY啊 &#x1f4ab;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f917;点赞&#x1f388;收藏⭐再看&#x1f4ab;养成习惯 ✨希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出…

【.NET全栈】.NET全栈学习路线

一、微软官方C#学习 https://learn.microsoft.com/zh-cn/dotnet/csharp/tour-of-csharp/ C#中的数据类型 二、2021 ASP.NET Core 开发者路线图 GitHub地址&#xff1a;https://github.com/MoienTajik/AspNetCore-Developer-Roadmap/blob/master/ReadMe.zh-Hans.md 三、路线…

书生浦语训练营2期-第二节课笔记作业

目录 一、前置准备 1.1 电脑操作系统&#xff1a;windows 11 1.2 前置服务安装&#xff08;避免访问127.0.0.1被拒绝&#xff09; 1.2.1 iis安装并重启 1.2.2 openssh安装 1.2.3 openssh服务更改为自动模式 1.2.4 书生浦语平台 ssh配置 1.3 补充&#xff08;前置服务ok…

电池二次利用走向可持续大循环周期的潜力和挑战(第一篇)

一、背景 当前&#xff0c;气候变化是全球可持续发展面临的重大挑战。缓解气候变化最具挑战性的目标是在本世纪中期实现碳中和&#xff08;排放量低到足以被自然系统安全吸收&#xff09;&#xff0c;其中电动汽车&#xff08;EV&#xff09;的引入是一项关键举措。电动汽车在…

小林coding图解计算机网络|基础篇01|TCP/IP网络模型有哪几层?

小林coding网站通道&#xff1a;入口 本篇文章摘抄应付面试的重点内容&#xff0c;详细内容还请移步&#xff1a; 文章目录 应用层(Application Layer)传输层(Transport Layer)TCP段(TCP Segment) 网络层(Internet Layer)IP协议的寻址能力IP协议的路由能力 数据链路层(Link Lay…

每日一题:用c语言中指针实现除2操作

目录 一、要求 二、实现代码 三、实现结果 四、关于指针的相关知识 一、要求 二、实现代码 #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> void change(int *i) {*i*i/2; }int main() {int i;printf("请您输入一个整数&#xff1a;");scanf(&quo…

LC 111.二叉树的最小深度

111. 二叉树的最小深度 给定一个二叉树&#xff0c;找出其最小深度。 最小深度是从根节点到最近叶子节点的最短路径上的节点数量。 说明&#xff1a; 叶子节点是指没有子节点的节点。 示例 1&#xff1a; 输入&#xff1a; root [3,9,20,null,null,15,7] 输出&#xff1a;…

LeetCode-124. 二叉树中的最大路径和【树 深度优先搜索 动态规划 二叉树】

LeetCode-124. 二叉树中的最大路径和【树 深度优先搜索 动态规划 二叉树】 题目描述&#xff1a;解题思路一&#xff1a;递归。return max(max(l_val, r_val) node.val, 0)解题思路二&#xff1a;0解题思路三&#xff1a;0 题目描述&#xff1a; 二叉树中的 路径 被定义为一条…

景联文科技提供高质量医疗健康AI大模型数据

医疗行业是典型的知识和技术密集型行业&#xff0c;其发展水平直接关系到国民健康和生命质量。 医疗健康AI大模型&#xff0c;作为人工智能的一个分支&#xff0c;能够通过学习大量的数据来生成新的数据实例&#xff0c;在医药研发、医学影像、医疗文本分析等都有广泛的应用前景…

基于单片机的无线红外报警系统

**单片机设计介绍&#xff0c;基于单片机的无线红外报警系统 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于单片机的无线红外报警系统是一种结合了单片机控制技术和无线红外传感技术的安防系统。该系统通过无线红外传感器实…

满足小体积/低功耗/低成本需求,世强硬创推出CGM解决方案

随着CGM的普及与更多具备性价比的国产产品上市&#xff0c;越来越多的企业开始布局CGM市场。 为此全球领先的硬件创新研发和供应服务平台世强硬创面向硬科技企业推出CGM&#xff08;连续血糖监测&#xff09;解决方案。 该方案可一站式解决企业开发需求&#xff0c;包括系统整…

vivado 系统监控器

系统监控器 (SYSMON) 模数转换器 (ADC) 用于测量硬件器件上的裸片温度和电压。 SYSMON 可通过片上温度和供电传 感器来监控物理环境。 ADC 可为各种应用提供高精度模拟接口。 请参阅下文 &#xff0c; 以获取有关特定器件架构的更多信息 &#xff1a; • 《 UltraScale …

前视声呐目标识别定位(五)-代码解析之修改声呐参数

前视声呐目标识别定位&#xff08;一&#xff09;-基础知识 前视声呐目标识别定位&#xff08;二&#xff09;-目标识别定位模块 前视声呐目标识别定位&#xff08;三&#xff09;-部署至机器人 前视声呐目标识别定位&#xff08;四&#xff09;-代码解析之启动识别模块 …

售价 25.98 万元起,远航 Y6、H8 焕新来袭,让高端豪华电动汽车真正“触手可及”

当下的新能源汽车市场中&#xff0c;各品牌竞争激烈&#xff0c;远航汽车通过一系列专属的权益优惠&#xff0c;以令人惊喜的25.98 万元起售价&#xff0c;远航 Y6 、H8成功打破高端豪华电动汽车价格壁垒&#xff0c;相比同类产品更具竞争力&#xff0c;为广大消费者带来了更轻…