C++数据结构--红黑树

news2024/11/19 9:22:02

目录

  • 一、红黑树的概念
  • 二、红黑树的性质
  • 三、红黑树的节点的定义
  • 四、红黑树结构
  • 五、红黑树的插入操作
    • 参考代码
  • 五、代码汇总

一、红黑树的概念

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

二、红黑树的性质

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

问题:为什么满足上面的性质,红黑树就能保证:其最长路径中节点个数不会超过最短路径节点个数的两倍?

因为根节点是黑色是固定的,并且不能有连续的红色节点,每条路径黑色节点的个数一样,也就是说最短路就是全黑,最长路径就是一黑一红相间,所以一条路径的红色节点要么和黑色节点一样多,要么少于黑色节点,即同一条路径黑色节点的占比是大于等于50%的,所以最长路径一定不超过最短路径的2倍。

三、红黑树的节点的定义

	//通过枚举定义红色和黑色的常量
	enum Colour
	{
		RED,
		BLACK
	};
	template <class K,class V>
	struct RBTreeNode
	{
	public:
		//红黑树存放的值
		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)
			, _col(RED)
		{}

	};

思考:在节点的定义中,为什么要将节点的默认颜色给成红色的?
这个问题就是关于插入节点的时候我们默认插入的是黑色的节点还是红色的节点了,根据红黑树的规则,每一条路径的黑色节点的个数是一样的,假如我们插入黑色节点,那么某条路径的黑色节点就多了一个,也就是说其它路径的黑色节点也要增加一个,但是我们只插入了一个节点,要令其它路径的黑色节点的数量都增加一个,这个操作的难度显然是很大的,但是如果我们默认插入一个红色节点,那么我们最多违反了根节点为黑色或者连续两个红色节点的规则,主要还是容易违反连续两个红色节点的规则,这个问题只会影响当前节点到祖先这条路径,不会影响其他路径的节点,所以处理起来会更简洁一些,所以我们默认插入的节点是红色的。

四、红黑树结构

在这里插入图片描述

五、红黑树的插入操作

红黑树是在二叉搜索树的基础上加上其平衡限制条件,因此红黑树的插入可分为两步:

  1. 按照二叉搜索树的规则插入新节点。
  2. 检测新节点插入后,红黑树的性质是否遭到破坏,如果是,就要通过变色加旋转操作维持红黑树的性质。

因为新节点的默认颜色是红色,因此:如果其双亲节点的颜色是黑色,没有违反红黑树任何性质,则不需要调整;但当新插入节点的双亲节点颜色为红色时,就违反了性质三不能有连在一起的红色节点,此时需要对红黑树分情况来讨论:

约定:cur为当前节点,p为父节点,g为祖父节点,u为叔叔节点。

情况一: cur为红,p为红,g为黑,u存在且为红。
在这里插入图片描述
情况二: cur为红,p为红,g为黑,u不存在/u存在且为黑。
因为旋转后需要把p变成黑色,所以p节点和p的父节点不能再出现连续两个红色节点,所以旋转+变色后就不用再沿祖先路径更新了。

在这里插入图片描述
情况一:
在这里插入图片描述
情况二:
在这里插入图片描述
情况三:
在这里插入图片描述
情况四:
在这里插入图片描述

参考代码

		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 (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);
			if (kv.first < parent->_kv.first)
			{
				parent->_left = cur;
				cur->_parent = parent;
			}
			else if (kv.first > parent->_kv.first)
			{
				parent->_right = cur;
				cur->_parent = parent;
			}

			//走到这里已经插入完成,后面是检查新插入的节点有没有破坏红黑树的规则的逻辑

			//检查新插入的节点是否满足红黑树的规则,如果不满足就要进行变色/变色+旋转
			//因为新插入的cur节点的颜色一定是红色的,当cur的父节点存在并且为红色
			//时说明出现了连续的两个红色节点,这时需要进行变色/变色+旋转,如果父节点
			// 不存在或者存在且为黑色时就无需再处理了。
			// 为什么父节点有可能不存在?因为这是一个循环,循环更新往祖先处理可能到达根节点,
			//到了根节点就无需再处理了
			while (parent && parent->_col == RED)
			{
				Node* grandfather = parent->_parent;

				if (parent == grandfather->_left)
				{
					Node* uncle = grandfather->_right;

					if (cur == parent->_left)
					{
						//             grandfather
						//     parent
						//cur

						//画图
						//叔叔存在且为红
						if (uncle && uncle->_col == RED)
						{
							//变色
							parent->_col = BLACK;
							uncle->_col = BLACK;
							grandfather->_col = RED;

							//继续沿祖先路径检查
							cur = grandfather;
							parent = cur->_parent;//这里曾经漏写了
						}
						//叔叔不存在或者存在且为黑
						else//(uncle==nullptr||uncle->_col==BLACK)
						{
							//单纯的左边高,进行右单旋+变色
							RotateR(grandfather);
							grandfather->_col = RED;
							parent->_col = BLACK;

							//旋转完之后无需再沿祖先路径处理
							break;
						}
					}
					else//cur == parent->_right
					{
						//              grandfather
						//       parent
						//              cur

						//画图
						//叔叔存在且为红
						if (uncle && uncle->_col == RED)
						{
							//变色
							parent->_col = BLACK;
							uncle->_col = BLACK;
							grandfather->_col = RED;

							//继续往上调整
							cur = grandfather;
							parent = cur->_parent;//这里曾经忘记写
						}
						//叔叔不存在或者存在且为黑
						else//(uncle==nullptr || uncle->_col == BLACK)
						{
							//左右双旋
							RotateL(parent);
							RotateR(grandfather);
							//变色
							grandfather->_col = RED;
							cur->_col = BLACK;

							//旋转后就无需再沿祖先路径检查了,具体原因画图理解
							break;
						}

					}
				}
				else//(parent == grandfather->_right)
				{
					Node* uncle = grandfather->_left;
					if (cur == parent->_right)
					{
						//          grandfather
						//                         parent
						//                                    cur

						//画图
						//叔叔存在且为红
						if (uncle && uncle->_col == RED)
						{
							//变色
							parent->_col = BLACK;
							uncle->_col = BLACK;
							grandfather->_col = RED;

							//继续沿祖先路径检查
							cur = grandfather;
							parent = cur->_parent;
						}
						//叔叔不存在或者存在且为黑
						else//(uncle==nullptr || uncle->_col == BLACK)
						{
							//单纯的右边高,进行左单旋+变色
							RotateL(grandfather);
							grandfather->_col = RED;
							parent->_col = BLACK;

							//旋转+变色后就不需要再沿祖先路径检查了
							break;
						}
					}
					else//(cur == parent->_left)
					{
						//          grandfather
						//                          parent
						//          cur

						//叔叔存在且为红
						if (uncle && uncle->_col == RED)
						{
							//变色
							parent->_col = BLACK;
							uncle->_col = BLACK;
							grandfather->_col = RED;
							
							//继续沿祖先路径检查
							cur = grandfather;
							parent = cur->_parent;
						}
						//叔叔不存在或者存在且为黑
						else//(uncle==nullptr || uncle->_col == BLACK)
						{
							//根据模型可知需要右左双旋+变色
							RotateR(parent);
							RotateL(grandfather);
							grandfather->_col = RED;
							cur->_col = BLACK;

							//旋转后就不需要再沿祖先路径检查了
							break;
						}
					}
				}
			}

			_num++;

			//最后记得把根节点的颜色改成黑色
			_root->_col = BLACK;
			return true;
		}
		//旋转的细节如果不清楚的话请看上一篇关于AVL树的旋转,红黑树的旋转和AVL树的旋转是一样的
		void RotateL(Node* parent)
		{
			assert(parent);
			Node* cur = parent->_right;
			Node* curleft = cur->_left;
			Node* parentParent = parent->_parent;

			parent->_right = curleft;
			cur->_left = parent;
			if (curleft)
			{
				curleft->_parent = parent;
			}
			parent->_parent = cur;

			if (parent == _root)
			{
				_root = cur;
				cur->_parent = nullptr;
			}
			else
			{
				if (parent == parentParent->_left)
				{
					parentParent->_left = cur;
				}
				else
				{
					parentParent->_right = cur;
				}
				cur->_parent = parentParent;
			}

		}

		void RotateR(Node* parent)
		{
			assert(parent);
			Node* cur = parent->_left;
			Node* curright = cur->_right;
			Node* parentParent = parent->_parent;

			parent->_left = curright;
			cur->_right = parent;
			if (curright)
			{
				curright->_parent = parent;
			}
			parent->_parent = cur;

			if (parent == _root)
			{
				_root = cur;
				cur->_parent = nullptr;
			}
			else
			{
				if (parent == parentParent->_left)
				{
					parentParent->_left = cur;
				}
				else
				{
					parentParent->_right = cur;
				}

				cur->_parent = parentParent;
			}

		}

五、代码汇总

#pragma once


#include <iostream>
using namespace std;
#include <assert.h>

namespace kb
{
	enum Colour
	{
		RED,
		BLACK
	};

	template <class K,class V>
	struct RBTreeNode
	{
	public:
		//红黑树存放的值
		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)
			, _col(RED)
		{}

	};

	template <class K,class V>
	class RBTree
	{
		typedef RBTreeNode<K, V> Node;
	public:
		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 (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);
			if (kv.first < parent->_kv.first)
			{
				parent->_left = cur;
				cur->_parent = parent;
			}
			else if (kv.first > parent->_kv.first)
			{
				parent->_right = cur;
				cur->_parent = parent;
			}

			//走到这里已经插入完成,后面是检查新插入的节点有没有破坏红黑树的规则的逻辑

			//检查新插入的节点是否满足红黑树的规则,如果不满足就要进行变色/变色+旋转
			//因为新插入的cur节点的颜色一定是红色的,当cur的父节点存在并且为红色
			//时说明出现了连续的两个红色节点,这时需要进行变色/变色+旋转,如果父节点
			// 不存在或者存在且为黑色时就无需再处理了。
			// 为什么父节点有可能不存在?因为这是一个循环,循环更新往祖先处理可能到达根节点,
			//到了根节点就无需再处理了
			while (parent && parent->_col == RED)
			{
				Node* grandfather = parent->_parent;

				if (parent == grandfather->_left)
				{
					Node* uncle = grandfather->_right;

					if (cur == parent->_left)
					{
						//             grandfather
						//     parent
						//cur

						//画图
						//叔叔存在且为红
						if (uncle && uncle->_col == RED)
						{
							//变色
							parent->_col = BLACK;
							uncle->_col = BLACK;
							grandfather->_col = RED;

							//继续沿祖先路径检查
							cur = grandfather;
							parent = cur->_parent;//这里曾经漏写了
						}
						//叔叔不存在或者存在且为黑
						else//(uncle==nullptr||uncle->_col==BLACK)
						{
							//单纯的左边高,进行右单旋+变色
							RotateR(grandfather);
							grandfather->_col = RED;
							parent->_col = BLACK;

							//旋转完之后无需再沿祖先路径处理
							break;
						}
					}
					else//cur == parent->_right
					{
						//              grandfather
						//       parent
						//              cur

						//画图
						//叔叔存在且为红
						if (uncle && uncle->_col == RED)
						{
							//变色
							parent->_col = BLACK;
							uncle->_col = BLACK;
							grandfather->_col = RED;

							//继续往上调整
							cur = grandfather;
							parent = cur->_parent;//这里曾经忘记写
						}
						//叔叔不存在或者存在且为黑
						else//(uncle==nullptr || uncle->_col == BLACK)
						{
							//左右双旋
							RotateL(parent);
							RotateR(grandfather);
							//变色
							grandfather->_col = RED;
							cur->_col = BLACK;

							//旋转后就无需再沿祖先路径检查了,具体原因画图理解
							break;
						}

					}
				}
				else//(parent == grandfather->_right)
				{
					Node* uncle = grandfather->_left;
					if (cur == parent->_right)
					{
						//          grandfather
						//                         parent
						//                                    cur

						//画图
						//叔叔存在且为红
						if (uncle && uncle->_col == RED)
						{
							//变色
							parent->_col = BLACK;
							uncle->_col = BLACK;
							grandfather->_col = RED;

							//继续沿祖先路径检查
							cur = grandfather;
							parent = cur->_parent;
						}
						//叔叔不存在或者存在且为黑
						else//(uncle==nullptr || uncle->_col == BLACK)
						{
							//单纯的右边高,进行左单旋+变色
							RotateL(grandfather);
							grandfather->_col = RED;
							parent->_col = BLACK;

							//旋转+变色后就不需要再沿祖先路径检查了
							break;
						}
					}
					else//(cur == parent->_left)
					{
						//          grandfather
						//                          parent
						//          cur

						//叔叔存在且为红
						if (uncle && uncle->_col == RED)
						{
							//变色
							parent->_col = BLACK;
							uncle->_col = BLACK;
							grandfather->_col = RED;
							
							//继续沿祖先路径检查
							cur = grandfather;
							parent = cur->_parent;
						}
						//叔叔不存在或者存在且为黑
						else//(uncle==nullptr || uncle->_col == BLACK)
						{
							//根据模型可知需要右左双旋+变色
							RotateR(parent);
							RotateL(grandfather);
							grandfather->_col = RED;
							cur->_col = BLACK;

							//旋转后就不需要再沿祖先路径检查了
							break;
						}
					}
				}
			}

			_num++;

			//最后记得把根节点的颜色改成黑色
			_root->_col = BLACK;
			return true;
		}

		size_t Size()
		{
			return _num;
		}

		bool Isbalance()
		{
			return _Isbalance(_root);
		}

		void Inorder()
		{
			_Inorder(_root);
		}

	private:

		bool CheckColour(Node* root, int blackNum, int BenchMark)
		{
			//走到空树说明这条路径已经走完了,需要检查黑色节点的个数和基准值相不相等
			if (root == nullptr)
			{
				//如果不相等,证明不平衡,返回false
				if (blackNum != BenchMark)
				{
					return false;
				}
				//如果相等,则说明本条路径没有出事,还要检查其它路径
				return true;
			}

			//如果出现连续红色节点证明这棵树出问题了,返回false
			if (root->_col == RED && root->_parent && root->_parent->_col == RED)
			{
				return false;
			}

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

			//递归检查所有路径的颜色
			return CheckColour(root->_left, blackNum, BenchMark)
				&& CheckColour(root->_right, blackNum, BenchMark);

		}

		bool _Isbalance(Node* root)
		{
			//空树可以认为是平衡的
			if (root == nullptr)
			{
				return true;
			}

			//根节点不是黑色说明这棵树出事了
			if (root->_col != BLACK)
			{
				return false;
			}

			//先算出一条路径的黑色节点的个数作为基准值,检查其它路径的黑色节点数目是否跟这个标准值
			//是否一样,如果不一样就证明这棵树不平衡了,那么如果这个基准值本身就是错的呢,那也没有关系,
			//只要有路径的黑色节点和其它路径不相等,就说明肯定有其中一条路径出问题了,至于是哪条路径
			//出问题就不重要了
			int BenchMark = 0;
			Node* cur = root;
			while (cur)
			{
				if (cur->_col == BLACK)
				{
					BenchMark++;
				}
				cur = cur->_left;
			}

			//检查所有路径中是否有连续红色节点和各路径中黑色节点的数目是否相等
			return CheckColour(root, 0, BenchMark);
		}

		void _Inorder(Node* root)
		{
			if (root == nullptr)
			{
				return;
			}

			_Inorder(root->_left);
			cout << root->_kv.second << " ";
			_Inorder(root->_right);
		}
		
		//旋转的细节如果不清楚的话请看上一篇关于AVL树的旋转,红黑树的旋转和AVL树的旋转是一样的
		void RotateL(Node* parent)
		{
			assert(parent);
			Node* cur = parent->_right;
			Node* curleft = cur->_left;
			Node* parentParent = parent->_parent;

			parent->_right = curleft;
			cur->_left = parent;
			if (curleft)
			{
				curleft->_parent = parent;
			}
			parent->_parent = cur;

			if (parent == _root)
			{
				_root = cur;
				cur->_parent = nullptr;
			}
			else
			{
				if (parent == parentParent->_left)
				{
					parentParent->_left = cur;
				}
				else
				{
					parentParent->_right = cur;
				}
				cur->_parent = parentParent;
			}

		}

		void RotateR(Node* parent)
		{
			assert(parent);
			Node* cur = parent->_left;
			Node* curright = cur->_right;
			Node* parentParent = parent->_parent;

			parent->_left = curright;
			cur->_right = parent;
			if (curright)
			{
				curright->_parent = parent;
			}
			parent->_parent = cur;

			if (parent == _root)
			{
				_root = cur;
				cur->_parent = nullptr;
			}
			else
			{
				if (parent == parentParent->_left)
				{
					parentParent->_left = cur;
				}
				else
				{
					parentParent->_right = cur;
				}

				cur->_parent = parentParent;
			}

		}

	private:
		Node* _root = nullptr;
		int _num = 0;//统计树的节点个数
	};

	void testRBTree(void)
	{
		RBTree<int, int> rbt;
		int arr[]= { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
		for (const auto& e : arr)
		{
			rbt.Insert(make_pair(e, e));
		}
		/*rbt.Inorder();*/
		cout << rbt.Size() << endl;

		bool ret = rbt.Isbalance();
		cout << ret << endl;

		cout << endl;

	}

	void testRBTree1(void)
	{
		RBTree<int, int> rbt;
		int N = 10000;
		srand((unsigned int)time(nullptr));
		for (size_t i=0;i<N;i++)
		{
			int e = rand();
			rbt.Insert(make_pair(e, e));
		}
		cout << rbt.Size() << endl;

		bool ret = rbt.Isbalance();
		cout << ret << endl;
		cout << "插入完成" << endl;

	}
}

以上就是红黑树的相关内容了,最重要的是要理解当红黑树插入元素后违反了红黑树的规则时该如何对节点进行旋转加变色来使这棵红黑树重新回到平衡的。至于删除节点的操作就不实现了,有兴趣的可以去看看《算法导论》这本书,里面有讲红黑树删除操作的。

好啦,以上就是今天想要跟大家谈的关于红黑树的最重要的内容了,你学会了吗?如果你感觉到有所帮助,那么点点小心心,点点关注呗,后期还会持续更新C++的相关知识哦,我们下期见啦!!!

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

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

相关文章

数字花园的指南针:微信小程序排名的提升之道

微信小程序&#xff0c;是一片数字花园&#xff0c;其中各种各样的小程序竞相绽放&#xff0c;散发出各自独特的芬芳。在这个花园中&#xff0c;排名优化就像是精心照料花朵的园丁&#xff0c;让我们一同走进这个数字花园&#xff0c;探寻如何提升微信小程序的排名优化&#xf…

Idea项目爆红

解决办法&#xff1a; 方案一&#xff1a;重新加载Maven依赖 方案二&#xff1a;清除缓存 方案三&#xff1a; 在当前项目下执行以下命令&#xff0c;重新生成.iml文件 mvn idea:module

Hadoop的安装和使用,Windows使用shell命令简单操作HDFS

1&#xff0c;Hadoop简介 Hadoop是一个能够对大量数据进行分布式处理的软件框架&#xff0c;并且是以一种可靠、高效、可伸缩的方式进行处理的&#xff0c;它具有以下几个方面的特性。 高可靠性。 高效性。 高可扩展性。 高容错性。 成本低。 运行在Linux平台上。 支持多种编程…

【Redis7】--2.十大数据类型

文章目录 Redis十大数据类型1.Key通用命令1.1keys *1.2EXISTS1.3DEL1.4EXPIRE1.5TTL1.6TYPE1.7DBSIZE1.8SELECT1.9MOVE1.10FLUSHDB1.11FLUSHALL1.12help1.13CONFIG 2.Redis十大数据类型2.1String2.1.1SET和GET2.1.2MSET和MGET2.1.3INCR、INCRBY2.1.4SETNX和SETEX2.1.5MSETNX2.1…

Android笔记(二十九):利用python自动生成多语言

背景 项目需要支持十几种多语言&#xff0c;而且每个版本的新功能ui都有很多地方需要多语言&#xff0c;如果手动添加非常耗时&#xff0c;于是设计了一个python脚本&#xff0c;通过excel表格转化多语言到项目values/strings文件内 步骤 android工程项目结构 脚本位于langu…

Unity实现用WASD控制一个物体前后左右移动-小白课程01

1 根据业务逻辑搭建场景 02 根据业务写代码 using System.Collections; using System.Collections.Generic; using UnityEngine;//实现让被挂在的物体往前移动 //按下W键往前移动&#xff0c;按下S键往后移动 public class RoleMove : MonoBehaviour { public float myspe…

Enterprise Architect15(EA) 工具栏,隐藏后显示快捷方式

没有工具栏 显示工具栏 快捷键&#xff1a;ctrl shift 3 或者Design-->点击ToolBox 工具栏中直接拖动即可创建对应的元素&#xff1a;

springboot集成qq邮箱

1.maven依赖 <!-- email依赖 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-mail</artifactId></dependency><dependency><groupId>org.springframework.boot</group…

seata的部署和集成:部署Seata的tc-server、微服务集成seata、TC服务的高可用和异地容灾

seata的部署和集成 一、部署Seata的tc-server 1.下载 首先我们要下载seata-server包&#xff0c;地址在http&#x1f615;/seata.io/zh-cn/blog/download.html 当然&#xff0c;课前资料也准备好了&#xff1a; 2.解压 在非中文目录解压缩这个zip包&#xff0c;其目录结构…

git 合并分支某次(commit)提交

需求&#xff1a;将develop分支某次提交合并到master上面&#xff0c;其他修改不同步&#xff1b; //切换到master分支 git checkout master //查看develop分支提交记录&#xff0c;获取对应记录哈希值&#xff1b; git log develop // 按上下按钮可以上下查询对应记录&#xf…

分享一个python实验室设备预约管理系统 实验室设备维修系统源码 lw 调试

&#x1f495;&#x1f495;作者&#xff1a;计算机源码社 &#x1f495;&#x1f495;个人简介&#xff1a;本人七年开发经验&#xff0c;擅长Java、Python、PHP、.NET、微信小程序、爬虫、大数据等&#xff0c;大家有这一块的问题可以一起交流&#xff01; &#x1f495;&…

【Word】页眉编辑小技巧

页眉编辑小技巧 1 奇偶页不同2 仅设置正文有页眉3 页眉设置信息为章节内容参考 1 奇偶页不同 2 仅设置正文有页眉 1、定位到目录页之后&#xff0c;点击“布局——分隔符——分节符中的下一页”&#xff0c;在目录页和正文之间插入一个分节符&#xff0c;使得目录页和正文成为…

YOLOv5算法改进(16)— 增加小目标检测层

前言&#xff1a;Hello大家好&#xff0c;我是小哥谈。小目标检测层是指在目标检测任务中用于检测小尺寸目标的特定网络层。由于小目标具有较小的尺寸和低分辨率&#xff0c;它们往往更加难以检测和定位。YOLOv5算法的检测速度与精度较为平衡&#xff0c;但是对于小目标的检测效…

[管理与领导-85]:IT基层管理者 - 核心技能 - 高效执行力 - 10 - 高效执行力的9个段位

目录 前言&#xff1a; 一段&#xff1a;准确执行&#xff0c;快速反应&#xff0c;坚决执行 &#xff08;态度很重要&#xff09; 二段&#xff1a;结果导向 苦劳过后&#xff0c;有功劳&#xff08;有结果很重要&#xff09; 三段&#xff1a;有始有终 主动反馈、有始有终…

Prometheus 监控指南:如何可靠地记录数字时间序列数据

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f405;&#x1f43e;猫头虎建议程序员必备技术栈一览表&#x1f4d6;&#xff1a; &#x1f6e0;️ 全栈技术 Full Stack: &#x1f4da…

《TCP/IP网络编程》阅读笔记--多播与广播

目录 1--多播 2--多播代码实例 3--广播 4--广播代码实例 1--多播 多播方式的数据传输是基于 UDP 完成的&#xff0c;多播数据包的格式与 UDP 数据包相同&#xff1b; 多播与 UDP 的区别&#xff1a;UDP 数据传输以单一目标进行&#xff0c;多播数据同时传递到加入&#xff…

【数据结构】红黑树的插入与验证

文章目录 一、基本概念1.时代背景2. 基本概念3.基本性质 二、实现原理1. 插入1.1变色1.2旋转变色①左旋②右旋③右左双旋④左右双旋 2.验证 源码总结 一、基本概念 1.时代背景 1972年鲁道夫拜尔(Rudolf Bayer)发明了一种数据结构&#xff0c;这是一种特殊的B树4阶情况。这些树…

基于SSM的学生公寓管理中心系统设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

Geotif.js读取tif元信息相关问题记录

起因是使用OL加载COG时&#xff0c;出现了不指定sources的max就一片黑的情况&#xff0c;所以需要读取tif真实波段值范围而不是靠比例设置颜色了。 使用geotiff.js可以读取tif的元信息&#xff0c;但当tif没有GDAL_METADATA这个key时就读不出来 然后找到了这个 乍一看简直完美…

普中 51 单片机点亮LED灯

普中 51 单片机 &#xff08;STC89C52RC&#xff09; LED / IO 将LED1进行闪烁操作 为啥要进行延时操作&#xff1f;依据人的肉眼余晖效应&#xff0c; 延时时间不能太短&#xff0c;否则就无法观察到 LED 闪烁 #include "reg52.h" typedef unsigned int u16; //对…