[C++]20.实现红黑树。

news2024/11/15 11:07:41

实现红黑树

  • 一.基本概念:
    • 1.红黑树的概念:
    • 2.红黑树的性质:
  • 二.实现红黑树:
    • 1.基本结构:
    • 2.插入节点的多种情况:
      • 1.叔叔存在且为红:
      • 2.叔叔不存在/存在且为黑(单旋+变色)
      • 3.叔叔不存在/存在且为黑(多旋(不是一个单一方向比较长)+变色)
      • 4.总体插入代码:
    • 3.红黑树的验证:
      • 1.验证搜索树:
      • 2.验证性质:
        • 1.根节点是黑色
        • 2. 没有连续的红色节点出现
        • 3.每一条路径的黑色节点个数相同
    • 4.红黑树和AVL树的比较:
      • 1. 随机值判断:
        • 1.100
        • 2.10000
        • 3.1000000
        • 4.10000000
      • 2.有序值判断:
        • 1.100
        • 2.10000
        • 3.1000000
        • 4.10000000
  • 三.map和set的封装:
    • 1.概念:
      • 1.set
      • 2.map
    • 2.基本结构:
      • 1.set
      • 2.map
    • 3.插入
      • 1.set
      • 2.map
      • 3.总结:
    • 4.迭代器实现:
      • 1.set
      • 2.map
      • 3.红黑树部分中实现迭代器的++ -- != *
        • 1.实现operator++
        • 2.实现operator--
    • 5.查找:
      • 1.map
    • 6.operator[]的重载
      • 1.map特有:
      • 2.operator[]重载
      • 3.insert的优化

一.基本概念:

1.红黑树的概念:

红黑树是一种二叉搜索树,但是在每一个存储节点上面增加了一个成员变量用来记录当前节点的颜色,可以是RED 或者 BLACK 。 通过从根节点到任意一个叶子节点的着色限制,红黑树可以确保没有任何一条路径会比最短路径的两倍还要长,因此红黑树是接近平衡的。

2.红黑树的性质:

1.每一个节点不是红色就是黑色。
2.根节点必须是黑色。
3.如果一个节点是红色它的两个孩子节点是黑色---->不存在连续的红节点。
4.一个根节点出去的所有路径,路径的黑色节点个树必须相同。
5.每一个叶子节点都是黑色的,这个叶子是nullptr不是实际已经开辟好的节点。

二.实现红黑树:

1.基本结构:

在这里插入图片描述

2.插入节点的多种情况:

1.叔叔存在且为红:

在这里插入图片描述

2.叔叔不存在/存在且为黑(单旋+变色)

在这里插入图片描述

1.新增节点是父亲的左边,并且父亲是爷爷的左边。
进行右旋+变色处理
2.新增节点是父亲的右边,并且父亲是爷爷的右边。
进行右旋+变色处理

3.叔叔不存在/存在且为黑(多旋(不是一个单一方向比较长)+变色)

在这里插入图片描述

1.新增节点是父亲的右边,并且父亲是爷爷的左边。
进行左旋+右旋+变色处理
2.新增节点是父亲的左边,并且父亲是爷爷的右边。
进行右旋+左旋+变色处理

4.总体插入代码:

template<class T>
class RBTree {
	typedef RBTreeNode<T> Node;
public:
	//1.插入:
	bool insert(pair<T, T> x)
	{
		Node* newnode = new Node(x);
		//1.开始_root == nullptr:
		if (_root == nullptr)
		{
			_root = newnode;
			_root->c = BLACK;
			return true;
		}
		//2.有节点的情况下进行插入:
		else
		{
			//2_1:向下找插入位置:
			Node* cur = _root;
			Node* parent = nullptr;
			while (cur)
			{
				if (x.first > cur->_date.first)
				{
					parent = cur;
					cur = cur->_right;
				}
				else if (x.first < cur->_date.first)
				{
					parent = cur;
					cur = cur->_left;
				}
				else
				{
					return false;
				}
			}

			//2_2:新增节点:
			if (x.first > parent->_date.first)
			{
				parent->_right = newnode;
				newnode->_parent = parent;
			}
			else if (x.first < parent->_date.first)
			{
				parent->_left = newnode;
				newnode->_parent = parent;
			}

			//确定一下新增节点:
			cur = newnode;

			//2_3:向上进行调整:
			while (parent && parent->c == RED)
			{
				Node* grandfather = parent->_parent;
				if (parent == grandfather->_left)
				{
					Node* uncle = grandfather->_right;
					//1.叔叔存在且为红:
					if (uncle && uncle->c == RED)
					{
						//1-1:变色:
						parent->c = BLACK;
						uncle->c = BLACK;
						grandfather->c = RED;
						//1-2:数据更新
						cur = grandfather;
						parent = cur->_parent;
					}
					//2.叔叔不存在/存在且为黑:
					else
					{
						//右旋+变色
						if (cur == parent->_left)
						{
							right_turn(grandfather);
							grandfather->c = RED;
							parent->c = BLACK;
						}
						//左旋+右旋+变色
						else
						{
							left_turn(parent);
							right_turn(grandfather);
							grandfather->c = RED;
							cur->c = BLACK;
						}
						break;
					}

					
				}

				else if (parent == grandfather->_right)
				{
					Node* uncle = grandfather->_left;
					//1.叔叔存在且为红:
					if (uncle && uncle->c == RED)
					{
						//1-1:变色:
						parent->c = BLACK;
						uncle->c = BLACK;
						grandfather->c = RED;
						//1-2:数据更新
						cur = grandfather;
						parent = cur->_parent;
					}

					//2.叔叔不存在/存在且为黑:
					else
					{
						else
					{
						//左旋+变色
						if (cur == parent->_right)
						{
							left_turn(grandfather);
							grandfather->c = RED;
							parent->c = BLACK;
						}
						//右旋+左旋+变色
						else
						{
							right_turn(parent);
							left_turn(grandfather);
							grandfather->c = RED;
							cur->c = BLACK;
						}

						break;
					}
					}

					
				}
			}

			//处理根节点的特殊情况:
			_root->c = BLACK;

			return true;
		}
	}

	//左旋:
	void left_turn(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;

		parent->_right = subRL;
		//特殊情况的判断:
		if (subRL != nullptr)
			subRL->_parent = parent;

		Node* ppNode = parent->_parent;
		subR->_left = parent;
		parent->_parent = subR;

		//当前的parent是子树还是根

		//1.作为根:
		if (ppNode == nullptr)
			_root = subR;
		//2.作为子树考虑左右
		else
		{
			if (ppNode->_left == parent)
				ppNode->_left = subR;
			else if (ppNode->_right == parent)
				ppNode->_right = subR;

			subR->_parent = ppNode;
		}
		
	}

	//右旋:
	void right_turn(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;

		parent->_left = subLR;
		//特殊情况的判断:
		if (subLR != nullptr)
			subLR->_parent = parent;

		Node* ppNode = parent->_parent;
		subL->_right = parent;
		parent->_parent = subL;

		//当前的parent是子树还是根

		//1.作为根:
		if (ppNode == nullptr)
			_root = subL;
		//2.作为子树考虑左右
		else
		{
			if (ppNode->_left == parent)
				ppNode->_left = subL;
			else if (ppNode->_right == parent)
				ppNode->_right = subL;

			subL->_parent = ppNode;
		}
		
	}


	//中序遍历:
	void _Inorder(Node* cur)
	{
		if (cur == nullptr)
			return;

		_Inorder(cur->_left);
		//cout << "date:" << cur->_date << "平衡因子:" << cur->_bf << endl;
		cout << (cur->_date).first << endl;
		_Inorder(cur->_right);
	}

	void Inorder()
	{
		if (_root == nullptr)
			return;
		_Inorder(_root);
	}

private:
	Node* _root=nullptr;
};

3.红黑树的验证:

1.验证搜索树:

1.遍历一个vector进行数据插入
2.打印中序遍历二叉树的结果。
3.观察结果是否有序就可以确定是否是一个搜索树。

void text_1()
{
	RBTree<int> T;
	vector<int> num = { 8,6,7,11,5,10,13,12,15 };

	for (auto e : num)
	{
		T.insert(make_pair(e,e));
	}
	
	T.Inorder();
}

在这里插入图片描述

2.验证性质:

1.每一个节点不是红色就是黑色。
2.根节点必须是黑色。
3.如果一个节点是红色它的两个孩子节点是黑色---->不存在连续的红节点。
4.一个根节点出去的所有路径,路径的黑色节点个树必须相同。
5.每一个叶子节点都是黑色的,这个叶子是nullptr不是实际已经开辟好的节点。

1.验证红黑树需要注意不要去验证结论,应该验证控制条件的存在。
2.结论:任意一条路径长度都不会大于最短路径的二倍.
控制条件:
1.根节点是黑色.
2.没有连续的红色节点出现。
3.每一条路径的黑色节点个数相同。

在这里插入图片描述

1.根节点是黑色

1.直接去判断根节点的颜色是不是黑色.

2. 没有连续的红色节点出现

1.当前节点的颜色是红节点,父亲是红节点就出现了连续的红节点.
2.出现了连续的红节点就返回false.

3.每一条路径的黑色节点个数相同

1.先去计算出来最左或者最右路径的黑色节点的个数.
2.进行传参在递归过程到叶子节点去判断计算路径和递归路径的黑色节点的个数是否相同.

bool _IsRBTree(Node* cur , int count , int& refnumber)
	{
		if (cur == nullptr)
		{
			//记录数据的判断:
			if (count == refnumber)
				return true;

			cout << "当前黑色节点的路径长度不匹配" << endl;
			return false;
		}

		//判断没有连续的红色节点出现:如果当前子节点为红,父节点就不可以是红。
		if (cur->c == RED && cur->_parent->c == RED)
		{
			cout<< "存在连续的红色节点出现问题" << endl;
			return false;
		}
		
		//记录长度:
		if (cur->c == BLACK)
			count++;

		return _IsRBTree(cur->_left,count,refnumber);
		return _IsRBTree(cur->_right, count, refnumber);
	}

	bool IsRBTree()
	{
		//1.根节点不是黑色判断:
		if (_root->c == RED)
		{
			cout << "根节点不是黑色" << endl;
			return false;
		}

		//2.计算最左或者最右路径的黑节点个数:
		Node* cur = _root;
		while (cur)
		{
			if (cur->c == BLACK)
				black++;
			cur = cur->_left;
		}

		int count = 0;
		return _IsRBTree(_root,count,black);
	}

4.红黑树和AVL树的比较:

1.插入相同的数据比较红黑树和AVL树.
2.比较插入数据的总时间
3.比较插入数据的高度
4.比较插入数据的旋转次数

1. 随机值判断:


void text_4()
{
	AVL_Tree<int> T1;
	RBTree<int> T2;

	vector<int> num;
	//生成随机值:

	int count = 100;
	srand(time(nullptr));

	for (int i = 0; i < count; i++)
	{
		num.push_back(i+rand());
	}

	int b1 = clock();
	for (auto e : num)
	{
		//cout << "e:" << e << endl;
		T1.Insert(make_pair(e, e));
	}
	int e1 = clock();
	for (auto e : num)
	{
		//cout << "e:" << e << endl;
		T2.insert(make_pair(e, e));
	}
	int e2 = clock();

	//插入时间:
	cout << "AVLinserttime :" << e1 - b1 << endl;
	cout << "RBinserttime :" << e2 - e1 << endl;
	//高度:
	cout << "AVLhight: " << T1.hight() << endl;
	cout << "RBhight: " << T2.hight() << endl;
	//旋转次数:
	cout << "AVLspin:" << T1.spin() << endl;
	cout << "RBspin:" << T2.spin() << endl;

}
1.100

在这里插入图片描述

2.10000

在这里插入图片描述

3.1000000

在这里插入图片描述

4.10000000

在这里插入图片描述

2.有序值判断:

void text_4()
{
	AVL_Tree<int> T1;
	RBTree<int> T2;

	vector<int> num;
	//生成随机值:

	int count = 10000000;
	//srand(time(nullptr));

	for (int i = 0; i < count; i++)
	{
		num.push_back(i);
	}

	int b1 = clock();
	for (auto e : num)
	{
		//cout << "e:" << e << endl;
		T1.Insert(make_pair(e, e));
	}
	int e1 = clock();
	for (auto e : num)
	{
		//cout << "e:" << e << endl;
		T2.insert(make_pair(e, e));
	}
	int e2 = clock();

	//插入时间:
	cout << "AVLinserttime :" << e1 - b1 << endl;
	cout << "RBinserttime :" << e2 - e1 << endl;
	//高度:
	cout << "AVLhight: " << T1.hight() << endl;
	cout << "RBhight: " << T2.hight() << endl;
	//旋转次数:
	cout << "AVLspin:" << T1.spin() << endl;
	cout << "RBspin:" << T2.spin() << endl;

}
1.100

在这里插入图片描述

2.10000

在这里插入图片描述

3.1000000

在这里插入图片描述

4.10000000

在这里插入图片描述

三.map和set的封装:

1.概念:

1.set

1.set底层使用红黑树进行数据的存储。
2.进一步的封装是因为BRTree的结构并不适合关联式容器set。
3.set同时需要实现多种容器的方法所以进行进一步的封装BRTree是非常有必要的。
4.进一步的封装有利于控制模板参数类型。

2.map

1.map底层使用红黑树进行数据的存储。
2.进一步的封装是因为BRTree的结构并不适合关联式容器map。
3.map同时需要实现多种容器的方法所以进行进一步的封装BRTree是非常有必要的。
4.进一步的封装有利于控制模板参数类型。

2.基本结构:

1.set

#include"RBTree.h"

namespace sfpy {
	template<class T>
	class myset {
	public:

	private:
		RBTree<T,T> _t;
	};
}

2.map

#include"RBTree.h"

namespace sfpy {
	template<class T , class V >
	class mymap {
	public:

	private:
		RBTree<T, pair<T, V>> _t;
	};
}

在这里插入图片描述

3.插入

1.set

在这里插入图片描述

如何统一数据大小的比较?
1.在set中实现一个仿函数并且重载operator()提取可以进行比较的数值。
2.直接实现一个内部类并且重载operator()方法并且考虑返回的类型值。

在这里插入图片描述

2.map

在这里插入图片描述

如何统一数据大小的比较?
1.在set中实现一个仿函数并且重载operator()提取可以进行比较的数值。
2.map中的问题:pair这个数据类型进行大小的比较是first大就大,在first相同的情况下,second大就大,类型本身进行大小的比较是不符合我们当前的要求的,进行仿函数重载提取数据直接进行比较。
3.直接实现一个内部类并且重载operator()方法并且考虑返回的类型值。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

3.总结:

1.分别在myset和mymap中去实现两个类并且重载operator()进行比较数据值的提取。
2.在operator()定义的时候要考虑返回值的类型是否是可以进行比较的数值。
3.优点:通过一个模板参数可以实例化两份不同的树,不需要自己手写。

4.迭代器实现:

1.迭代器本质就是对指针的二次封装,并且重载适合的方法,为什么需要进行二次封装?
2.因为:本身的结构并不可以满足++ – * 等需求!
2.对于链表,顺序表结构,指针和迭代器进行++ – * 操作效果相同不需要非常复杂的进行封装。
3.对于set和map这样的结构底层是通过红黑树进行实现的,就红黑树来说没有好的方法提供给我们去实现++ – * 等操作。

1.set

namespace sfpy {
	template<class T>
	class myset {
	public:
		struct bitter {
			const T& operator()(const T& x)
			{
				return x;
			}
		};

		//1.插入:
		bool _insert(T x)
		{
			return _t.insert(x);
		}

		//2.迭代器:
		typedef typename RBTree<T, T, bitter>::_iterator iterator;

		iterator begin()
		{
			return _t.look_left();
		}

		iterator end()
		{
			return nullptr;
		}

	private:
		RBTree<T,T, bitter> _t;
	};
}

2.map

namespace sfpy {
	template<class T , class V >
	class mymap {
	public:
		struct bitter {
			const T operator()(const pair<T,V>& x)
			{
				return x.first;
			}
		};

		//1.插入:
		bool _insert(pair<T, V> x)
		{
			return _t.insert(x);
		}

		//2.迭代器:
		typedef  typename RBTree<T, pair<T, V>, bitter>::_iterator iterator;

		iterator begin()
		{
			return _t.look_left();
		}

		iterator end()
		{
			return nullptr;
		}

	private:
		RBTree<T, pair<T, V>, bitter> _t;
	};
}

3.红黑树部分中实现迭代器的++ – != *

1.实现operator++

实现operator++
1.中序遍历:左子树 根节点 右子树
2.假设:当前节点是8说明左树已经遍历完了
–>判断当前右子树为不为空
---->右子树不是空找右子树的最左节点。
3.假设:当前节点是11,11(cur)是父亲(parent)的右
可以正常回去—>进行cur和parent的更新向上找下一个节点
—>直到parent->left == cur 说明当前的parent节点还没有遍历到。

在这里插入图片描述

2.实现operator–

实现operator–
1.++ 找右数的最左节点 ,–找左数的最右节点。
2.其他内容保持和++相反。

template<class T , class ret , class ptr>
struct RBT_Iterator {
	typedef RBTreeNode<T> Node;
	//返回一个迭代器类型
	typedef RBT_Iterator<T,ret,ptr> self;
	Node* node;

	RBT_Iterator(Node* x = nullptr)
		:node(x)
	{}

	self& operator++()
	{
		//左 中 右
		if (node->_right)
		{
			Node* cur = node->_right;
			//Node* cur = node;
			//找当前节点的最左节点:
			while (cur->_left)
			{
				cur = cur->_left;
			}
			node = cur;
		}
		//cur->right 没有的!
		else
		{

			Node* cur = node;
			Node* parent = cur->_parent;
			while (parent && cur == parent->_right)
			{
				cur = parent;
				parent = cur->_parent;
			}
			node = parent;
		}

		return *this;
	}

	self& operator--()
	{
		//右 中 左
		if (node->_left)
		{
			Node* cur = node->_left;
			//找当前节点的最右节点:
			while (cur->_right)
			{
				cur = cur->_right;
			}
			node = cur;
		}
		//cur->right 没有的!
		else
		{

			Node* cur = node;
			Node* parent = cur->_parent;
			while (parent && cur == parent->_left)
			{
				cur = parent;
				parent = cur->_parent;
			}
			node = parent;
		}
		return *this;
	}

	ret operator*()
	{
		return node->_date;
	}

	ptr operator->()
	{
		return &(node->_date);
	}

	bool operator!=(const self& x)
	{
		return node != x.node;
	}
};

template<class K , class V , class Bitter>
class RBTree {
	typedef RBTreeNode<V> Node;
public:
	typedef RBT_Iterator<V, const V& ,const V*> _iterator;
	//1.插入:

5.查找:

1.我们的set和map在封装红黑树的时候对当前封装的红黑树的类模板进行重新的规划。
2.对于set :RBTree<T,T, bitter> _t; ,set不需要实现find set存数据所决定。
3. 对于map ==RBTree<T, pair<T, V>, bitter> _t;==主要是为了方便实现find的查找功能。

1.map

//3.查找:
		V find(T x)
		{
			pair<T, V> ret = _t._find(make_pair(x, V()));
			return ret.second;
		}


	//找数据:
	V _find(V x)
	{
		Bitter kot;
		Node* cur = _root;
		Node* parent = nullptr;
		while (cur)
		{
			if (kot(x) > kot(cur->_date))
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (kot(x) < kot(cur->_date))
			{
				parent = cur;
				cur = cur->_left;
			}
			else
			{
				return cur->_date;
			}
		}

		return V();
	}

6.operator[]的重载

1.map特有:

1.operator[]是通过修改insert返回值类型为pair<iterator,bool>类型进行的一个实现:
2.返回值ret接受到返回值,访问迭代器中并且因为迭代器重载了operator->拿到对应的数据。
3.注意:新增节点和查询节点的一个区别,通过新增一个pair类型的数据去进行查询,查询到就就返回查询到的节点的迭代器,查询不到就返回新增。

2.operator[]重载

//4.重载operator[]--->insert进行重写
		V& operator[](T x)
		{
			pair<iterator,bool> ret =  _t.insert(make_pair(x,V()));
			return ret.first->second;
		}

3.insert的优化

//1.插入:
	pair<_iterator,bool> insert(V x)
	{
		Node* newnode = new Node(x);
		Bitter kot;
		//1.开始_root == nullptr:
		if (_root == nullptr)
		{
			_root = newnode;
			_root->c = BLACK;
			return make_pair(_iterator(newnode), true);
		}
		//2.有节点的情况下进行插入:
		else
		{
			//2_1:向下找插入位置:
			Node* cur = _root;
			Node* parent = nullptr;
			while (cur)
			{
				if (kot(x) > kot(cur->_date))
				{
					parent = cur;
					cur = cur->_right;
				}
				else if (kot(x) < kot(cur->_date))
				{
					parent = cur;
					cur = cur->_left;
				}
				else
				{
					return make_pair(_iterator(cur), false);
				}
			}

			//2_2:新增节点:
			if (kot(x) > kot(parent->_date))
			{
				parent->_right = newnode;
				newnode->_parent = parent;
			}
			else if (kot(x) < kot(parent->_date))
			{
				parent->_left = newnode;
				newnode->_parent = parent;
			}

			//确定一下新增节点:
			cur = newnode;

			//2_3:向上进行调整:
			while (parent && parent->c == RED)
			{
				Node* grandfather = parent->_parent;
				if (parent == grandfather->_left)
				{
					Node* uncle = grandfather->_right;
					//1.叔叔存在且为红:
					if (uncle && uncle->c == RED)
					{
						//1-1:变色:
						parent->c = BLACK;
						uncle->c = BLACK;
						grandfather->c = RED;
						//1-2:数据更新
						cur = grandfather;
						parent = cur->_parent;
					}
					//2.叔叔不存在/存在且为黑:
					else
					{
						//右旋+变色
						if (cur == parent->_left)
						{
							right_turn(grandfather);
							grandfather->c = RED;
							parent->c = BLACK;
						}
						//左旋+右旋+变色
						else
						{
							left_turn(parent);
							right_turn(grandfather);
							grandfather->c = RED;
							cur->c = BLACK;
						}
						break;
					}

					
				}

				else if (parent == grandfather->_right)
				{
					Node* uncle = grandfather->_left;
					//1.叔叔存在且为红:
					if (uncle && uncle->c == RED)
					{
						//1-1:变色:
						parent->c = BLACK;
						uncle->c = BLACK;
						grandfather->c = RED;
						//1-2:数据更新
						cur = grandfather;
						parent = cur->_parent;
					}

					//2.叔叔不存在/存在且为黑:
					else
					{
						//左旋+变色
						if (cur == parent->_right)
						{
							left_turn(grandfather);
							grandfather->c = RED;
							parent->c = BLACK;
						}
						//右旋+左旋+变色
						else
						{
							right_turn(parent);
							left_turn(grandfather);
							grandfather->c = RED;
							cur->c = BLACK;
						}

						break;
					}

					
				}
			}

			//处理根节点的特殊情况:
			_root->c = BLACK;
			return make_pair(_iterator(newnode), true);
		}
	}

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

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

相关文章

【计算机网络篇】计算机网络的定义和分类

文章目录 &#x1f354;什么是计算机网络&#x1f5c3;️计算机网络的分类⭐按交换方式分类⭐按使用者分类⭐按传输介质分类⭐按覆盖范围分类⭐按拓扑结构分类 &#x1f6f8;小结 &#x1f354;什么是计算机网络 计算机网络是指将多台计算机或其他网络设备通过通信链路连接起来…

C语言 ——常量

3, 常量 3.1 什么是常量 ​ 程序运行的过程中&#xff0c;其值永远不会发生改变的数据 3.2 常量的分类 3.3 练习 ​ 给你一组数据&#xff0c;说出下面每组数据中&#xff0c;分别是哪种类型的常量 3.4 细节补充 实型常量的小细节&#xff1a; * 小数点前后&#xff0c;如果…

【数据结构】二叉树---AVL树的实现

目录 一. 什么是AVL树 二. AVL树的结点结构定义 三. AVL树的动态平衡法 1. 左单旋转 --- RL(RotateLeft) 型调整操作 2. 右单旋转 --- RR(RotateRight) 型调整操作 3. 先左后右双旋转 --- RLR (RotateLeftRight) 型调整操作 4. 先右后左双旋转 --- RRL (RotateRightL…

python工具方法 47 基于paddleseg将目标检测数据升级为语义分割数据

在进行项目研究时,通常需要搜集开源数据集。但是所能搜集到的数据集通常会存在形式上的差异,比如我想要的是语义分割数据,而搜集到的数据集却是目标检测数据;在这种情况下所搜集的数据就完成没有利用价值了么?不,其还存在价值,我们可以通过模型训练对数据标签的标注粒度…

【学习】python装饰器

这篇文章讲的太清楚了&#xff01;非常好好好&#xff01;&#xff01;&#xff01;Python】一文弄懂python装饰器&#xff08;附源码例子&#xff09; 在他的基础上加了一些自己的话&#xff0c;便于自己理解。 注意的点&#xff1a; func 表示函数对象&#xff0c;可以将其…

智慧公厕建设,助力打造宜居、韧性、可持续的智慧城市

公共厕所作为智慧城市的重要组成部分&#xff0c;对于城市的高质量发展起着至关重要的作用。智慧公厕建设旨在通过全面监测、控制和管理公共厕所&#xff0c;实现多方面功能&#xff0c;包括公共厕所环境监测与调控、厕位占用监测与引导、消耗品监测与缺失提示、安全防范与管理…

使用OCC进行旋转扫掠

旋转扫掠是将物体以某一个坐标轴为参照&#xff0c;按照指定的角度旋转生成新的图形的过程 这里使用面的案例&#xff0c;使用线的逻辑处理其实是一样的 //构造旋转轴 gp_Ax1 anAxis; //设置轴的原点 anAxis.SetLocation(0,0,0); //设置轴的方向 anAxis.SetDirection(gp_Dir(0…

N3-Chitosan N3 叠氮修饰壳聚糖 改性叠氮 CS-Azide

碳水科技&#xff08;Tanshtech&#xff09;可以提供壳聚糖衍生物 1.壳聚糖的各种改性(NH2/COOH/SH/N3/MAL-Chitosan等) 2.各种靶向小分子修饰壳聚糖&#xff08;Biotin/FA/cRGD-Chitosan等&#xff09; 3.各种荧光标记壳聚糖(FITC/RB/CY-Chitosan等) 4.壳聚糖和各种聚合物…

基于springboot实现数据资产管理系统项目【项目源码+论文说明】计算机毕业设计

基于springboot实现数据资产管理系统演示 摘要 固定资产管理系统主要是完成对系统用户管理、资产信息管理、资产变更管理、资产用途管理、资产类别管理和资产增减管理。因为利用本系统管理员可以直接录入信息&#xff0c;修改信息&#xff0c;删除信息&#xff0c;并且若在录入…

【学一点RISC-V】RISC-V IMSIC

IMSIC RISC-V AIA 文档 第三章 Incoming MSI Controller (IMSIC) 传入 MSI 控制器&#xff08;IMSIC&#xff09;是一个可选的 RISC-V 硬件组件&#xff0c;与 hart 紧密相连&#xff0c;每个 hart 有一个 IMSIC。IMSIC 接收并记录 Hart 的传入消息信号中断 (MSI)&#xff0c;并…

Ubuntu下txt中文显示乱码问题常规解决方法

在正常使用ubuntu 文档时&#xff0c;突然发现txt文档出现&#xff0c;如下情况 无法正常观看&#xff0c;后来搜了一下发现是gedit 没有对应打开文件的编码格式&#xff0c;Ubuntu用的是utf-8,所以打开会有乱码&#xff01;初始没有GBK和GB2312&#xff0c;把GBK和GB2312添加…

Python:函数的形参与实参

注意&#xff1a;本文引用自专业人工智能社区Venus AI 更多AI知识请参考原站 &#xff08;[www.aideeplearning.cn]&#xff09; 函数基本概念 在Python中&#xff0c;函数是一种将代码封装以进行重复使用的机制。它们允许你定义一段代码&#xff0c;以便在程序的多个位置调…

微信小程序的手工艺品定制商城溯源交易系统

管理员通过点击后台管理&#xff0c;进入页面可以获取首页、个人中心、用户管理、商品分类管理、商品信息管理、我的定制管理、我的预约管理、系统管理、订单管理等功能模块&#xff0c;进行相对应操作 用户登录到小程序首页可以查看首页、商品信息、工艺品资讯、购物车、我的等…

OpenHarmony教程指南-性能示例

介绍 本示例集成了条件渲染、动态加载以及HiDumper等场景来介绍如何提升应用性能。 效果预览 HiDumper使用说明&#xff1a; 1.点击性能示例主页的HiDumper按钮&#xff0c;进入HiDumper查看组件信息场景页。 1.点击HiDumper查看组件信息场景页的查看应用组件树进入场景页。…

msf端口转发达到远程连接

一般云服务器上搭建msf没办法远程连接这样很苦恼 这里可以使用端口转发流量来实现 1.首先确认目标机开启了远程连接 然后使用命令将对方3389的端口转发到我们的任意监听端口 portfwd add -l 6666 -p 3389 -r 192.168.78.129 转发成功后ifconfig一下发现已经变化了 然后直接…

Python使用openpyxl库或pandas库创建.xlsx格式的Excel文件,并向文件不同的sheet按行或按列写入内容

import openpyxl# 创建-一个Workbook对象 wb openpyxl.Workbook()# 创建多个工作表 sheet1 wb.active sheet1.title "s1"sheet2 wb.create_sheet("s2")# 在不同的工作表中写入数据 sheet1["A1"] Data for Sheet1 sheet1["A2"] D…

STM32 利用FlashDB库实现在线扇区数据管理不丢失

STM32 利用FlashDB库实现在线扇区数据管理不丢失 &#x1f4cd;FalshDB地址:https://gitee.com/Armink/FlashDB ✨STM32没有片内EEPROM这样的存储区&#xff0c;虽然有备份寄存器&#xff0c;仅可以实现对少量数据的频繁存储&#xff0c;但是依赖备份电源&#xff08;BAT引脚&a…

xcode15,个推推送SDK闪退问题处理办法

个推iOS推送SDK最新版本 优化了xcode15部分场景下崩溃问题&#xff0c;以及回执上传问题&#xff0c;近期您的应用有发版计划&#xff0c;建议更新SDK&#xff1a; 1&#xff09;GTSDK更新到3.0.5.0以及以上版本&#xff1b; 2&#xff09;GTCommonSDK更新到3.1.0.0及以上版本…

【PCL】(二十八)点云超体素分割

&#xff08;二十九&#xff09;点云超体素分割 论文&#xff1a;Voxel Cloud Connectivity Segmentation - Supervoxels for Point Clouds supervoxel_clustering.cpp #include <pcl/console/parse.h> #include <pcl/point_cloud.h> #include <pcl/point_ty…

Linux:kubernetes(k8s)prestop事件的使用(11)

他的作用是在结束pod容器之后进行的操作 apiVersion: v1 # api文档版本 kind: Pod # 资源对象类型 metadata: # pod相关的元数据&#xff0c;用于描述pod的数据name: nginx-po # pod名称labels: # pod的标签type: app #这个是随便写的 自定义的标签version: 1.0.0 #这个…