红黑树(万字图文详解)

红黑树

  • 1. 红黑树的概念
  • 2. 红黑树的性质
  • 3. 红黑树节点的定义
  • 4. 红黑树结构
  • 5. 红黑树的插入操作
    • 5.1 按照二叉搜索的树规则插入新节点
    • 5.2 检测新节点插入后,红黑树的性质是否造到破坏
      • 5.2.1 情况一: cur为红,p为红,g为黑,u存在且为红
      • 5.2.2 情况二: cur为红(此时只需要单旋),p为红,g为黑,u不存在/u存在且为黑
      • 5.2.3 情况三: cur为红(此时需要进行双旋变成情况二),p为红,g为黑,u不存在/u存在且为黑
  • 完整代码
  • 6. 红黑树的验证
    • 1. 检测其是否满足二叉搜索树(中序遍历是否为有序序列)
    • 2. 检测其是否满足红黑树的性质
  • 7. 红黑树与AVL树的比较
  • 8. 红黑树的应用

注:本文的是在理解AVL树的基础上进行讲解的!


AVL树的实现(万字图文详解)


1. 红黑树的概念

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

2. 红黑树的性质

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

在这里插入图片描述

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

  1. 红黑树中的性质确保了任意路径上的黑色节点数量相等,这是红黑树能够保持平衡的关键。根据这个性质,我们可以证明红黑树的最长路径中的节点个数不会超过最短路径的节点个数的两倍。
  2. 假设红黑树的最短路径上的黑色节点数量为k。由于性质5,任意路径上的黑色节点数量相等,所以最长路径上的黑色节点数量不能少于k个。
  3. 现在我们来看最长路径上的节点个数。由于性质4,红色节点的两个子节点都是黑色的,因此最长路径上不能有连续的红色节点。而对于红黑树而言,最长路径上的红色节点数量最多为k,因为最长路径上的黑色节点数量不能少于k个。所以最长路径上的节点个数最多为2k,其中k个是黑色节点,k个是红色节点。
  4. 综上所述,红黑树的最长路径中的节点个数不会超过最短路径的节点个数的两倍,即最长路径上的节点个数最多为2k,其中k为最短路径上的黑色节点数量
  5. 这个性质保证了红黑树的高度始终保持在较小的范围内,从而保持了树的平衡性。因为红黑树的高度与最长路径上的节点个数成正比,所以最长路径的节点个数的上限为最短路径的节点个数的两倍,确保了红黑树的平衡性和高效性。

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;
	int _bf;
	RBTreeNode(const pair<K, V>& kv)
		:_left(nullptr)//左孩子
		, _right(nullptr)//右孩子
		, _parent(nullptr)//父亲
		, _kv(kv)//存储键值对的成员变量
		, _col(RED)//默认红色
	{}
};

具体解释如下:

  • _left:指向左子节点的指针。
  • _right:指向右子节点的指针。
  • _parent:指向父节点的指针。
  • _kv:存储键值对的成员变量。这里使用了 pair<K, V> 类型来表示键值对,其中 K 是键的类型,V 是值的类型。
  • _col:表示节点的颜色。这里使用了 Colour 枚举类型来表示,可能的取值为 RED 或 BLACK,其中 RED 表示红色,BLACK 表示黑色。
  • _bf:平衡因子,用于平衡二叉树的调整。

4. 红黑树结构

为了后续实现关联式容器简单,红黑树的实现中增加一个头结点,因为根节点必须为黑色,为了与根节点进行区分,将头结点给成黑色,并且让头结点的 pParent 域指向红黑树的根节点,pLeft域指向红黑树中最小的节点,_pRight域指向红黑树中最大的节点
如下:
在这里插入图片描述

5. 红黑树的插入操作

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

5.1 按照二叉搜索的树规则插入新节点

#pragma once
#include <iostream>
using namespace std;

//枚举类型变量
enum Colour
{
	RED,  //红
	BLACK //黑
};

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;
	int _bf;
	RBTreeNode(const pair<K, V>& kv)
		:_left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _kv(kv)
		, _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 (cur->_kv.first < kv.first)
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (cur->_kv.first > kv.first)
			{
				parent = cur;
				cur = cur->_left;
			}
			else
			{
				return false;
			}
		}

		cur = new Node(kv);
		cur->_col = RED;//!应该能去掉
			if (parent->_kv.first < kv.first)
			{
				parent->_right = cur;
				cur->_parent = parent;
			}
			else
			{
				parent->_left = cur;
				cur->_parent = parent;
			}

			
			红黑树开整
			//1.cur为红,parent为红,grandfather为黑,uncle存在且为红
			//解决方法:p,u变成黑,g变成红,c=g,继续向上调整
			//2.cur为红,parent为红,grandfather为黑,uncle不存在 or 存在且为黑
			//解决方法: 单旋:p变红,g变黑,//双旋:c变黑,g变红

private:
	Node* _root = nullptr;
};

5.2 检测新节点插入后,红黑树的性质是否造到破坏

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

在这里插入图片描述
约定:cur为当前节点,p为父节点,g为祖父节点,u为叔叔节点

5.2.1 情况一: cur为红,p为红,g为黑,u存在且为红

解决方法:将p,u改为黑,g改为红,然后把g当成cur,继续向上调整。

在这里插入图片描述

5.2.2 情况二: cur为红(此时只需要单旋),p为红,g为黑,u不存在/u存在且为黑

在这里插入图片描述

在这里插入图片描述
解决方法:

p为g的左孩子,cur为p的左孩子,则进行右单旋转;相反,
p为g的右孩子,cur为p的右孩子,则进行左单旋转
p、g变色–p变黑,g变红

在这里插入图片描述


在这里插入图片描述


在这里插入图片描述


5.2.3 情况三: cur为红(此时需要进行双旋变成情况二),p为红,g为黑,u不存在/u存在且为黑

在这里插入图片描述
解决方法:

  • p为g的左孩子,cur为p的右孩子,则针对p做左单旋转,再对g右单旋即可完成调整;
  • 相反,
    p为g的右孩子,cur为p的左孩子,则针对p做右单旋转
    ,再对g左单旋即可完成调整

在这里插入图片描述


在这里插入图片描述

完整代码

#pragma once
#include <iostream>
using namespace std;

//枚举类型变量
enum Colour
{
	RED,  //红
	BLACK //黑
};

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;
	int _bf;
	RBTreeNode(const pair<K, V>& kv)
		:_left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _kv(kv)
		, _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 (cur->_kv.first < kv.first)
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (cur->_kv.first > kv.first)
			{
				parent = cur;
				cur = cur->_left;
			}
			else
			{
				return false;
			}
		}

		cur = new Node(kv);
		cur->_col = RED;//!应该能去掉
			if (parent->_kv.first < kv.first)
			{
				parent->_right = cur;
				cur->_parent = parent;
			}
			else
			{
				parent->_left = cur;
				cur->_parent = parent;
			}

			
			红黑树开整
			//1.cur为红,parent为红,grandfather为黑,uncle存在且为红
			//解决方法:p,u变成黑,g变成红,c=g,继续向上调整
			//2.cur为红,parent为红,grandfather为黑,uncle不存在 or 存在且为黑
			//解决方法: 单旋:p变红,g变黑,//双旋:c变黑,g变红



			//parent不为空,且parent->_col==RED(这样保证肯定有祖先,也就是grandfather不为空)
			while (parent && parent->_col == RED)
			{
				//    g
				//  p   u
				//c
				Node* grandfather = parent->_parent;
				if (parent == grandfather->_left)
				{
					//    g
				    //  p   u
				    //c
					Node* uncle = grandfather->_right;
					if (uncle && uncle->_col == RED)
					{
						parent->_col = uncle->_col = BLACK;
						grandfather->_col = RED;
						
						//继续往上更新处理
						cur = grandfather;
						parent = cur->_parent;//这儿parent更新完之后可能为空,为空就结束
					}
					else//uncle不存在 or 存在且为黑
					{   //单旋
						//    g
					    //  p   u
					    //c
						if (cur == parent->_left)
						{
							RotateR(grandfather);
							parent->_col = BLACK;//p变黑
							grandfather->_col = RED;//g变红
						}
						else if (cur == parent->_right)
						{
							//双旋
							//    g
							//  p   u
							//    c
							RotateL(parent);
							RotateR(grandfather);
							cur->_col = BLACK;
							grandfather->_col = RED;
						}

						break;
					}
					
				}
				//    g
				//  u   p
				//        c
				else//  parent == grandfather->_left
				{
					Node* uncle = grandfather->_left;
					if (uncle && uncle->_col == RED)
					{
						parent->_col = uncle->_col = BLACK;
						grandfather->_col = RED;

						//继续往上更新处理
						cur = grandfather;
						parent = cur->_parent;
					}

					else//(uncle不存在 or 存在且为黑)
					{   //单旋
						//    g         
						//  u   p           
						//        c    
						if (cur == parent->_right)
						{
							RotateL(grandfather);
							parent->_col = BLACK;//p变黑
							grandfather->_col = RED;//g变红
						}	
						else//cur == parent->_left 
						{
							//双旋
							//    g
							//  u   p
							//    c  
							RotateR(parent);
							RotateL(grandfather);
							cur->_col = BLACK;
							grandfather->_col = RED;
						}
						break;
					}
				}

			}
			//管你根节点更新成啥了,管你个求,直接把_root->_col改成黑色
			_root->_col = BLACK;
		return true;
	}



	//左单旋
	//(1.父亲节点的右边等于右孩子的左边; 2.右孩子的左边等于父亲节点)
	//【把右孩子的左边给给父亲节点的右边; 2.再把父亲节点给给右孩子的左边】
	void RotateL(Node* parent)
	{


		Node* SubR = parent->_right;
		Node* SubRL = SubR->_left;

		//旋转链接
		parent->_right = SubRL;
		SubR->_left = parent;

		Node* Parent_Parent = parent->_parent;

		parent->_parent = SubR;
		if (SubRL)
		{
			SubRL->_parent = parent;
		}

		//和父节点的父节点链接
		if (_root == parent)
		{
			_root = SubR;
			SubR->_parent = nullptr;
		}
		else
		{
			if (Parent_Parent->_left == parent)
			{
				Parent_Parent->_left = SubR;
			}
			else
			{
				Parent_Parent->_right = SubR;
			}
			SubR->_parent = Parent_Parent;
		}
		
	}

	//右单旋
	void RotateR(Node* parent)
	{

		Node* SubL = parent->_left;
		Node* SubLR = SubL->_right;

		//旋转链接
		//动一个节点就把他的父亲也变动
		parent->_left = SubLR;
		if (SubLR)//SubLR可能为空
		{
			SubLR->_parent = parent;
		}

		Node* Parent_Parent = parent->_parent;

		SubL->_right = parent;
		parent->_parent = SubL;

		//和父节点的父节点链接
		if (_root == parent)
		{
			_root = SubL;
			SubL->_parent = nullptr;
		}
		else
		{
			if (Parent_Parent->_left == parent)
			{
				Parent_Parent->_left = SubL;
			}
			else
			{
				Parent_Parent->_right = SubL;
			}
			SubL->_parent = Parent_Parent;//链接
		}
		
	}


	void InOrder()
	{
		_InOrder(_root);
		cout << endl;
	}

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

		_InOrder(root->_left);
		cout << root->_kv.first << " ";
		_InOrder(root->_right);
	}

	//根节点到当前这条路径的黑色节点个数
	bool Check(Node *root,int blacknum,const int RefVal)
	{
		if (root == nullptr)
		{
			if (blacknum != RefVal)
			{
				return false;
			}
			return true;
		}
		//直接反向检查,儿子很复杂,父亲只有一个
		if (root->_col == RED && root->_parent->_col == RED)
		{
			cout<<"有连续的红节点" << endl;
			return false;
		}
		if (root->_col == BLACK)
		{
			blacknum++;
		}
		return Check(root->_left, blacknum, RefVal) && Check(root->_right, blacknum, RefVal);
	}

	bool IsBalance()
	{
		if (_root == nullptr)
		{
			return true;
		}
		if (_root->_col == RED)
		{
			return false;
		}


		int blacknum = 0;//记录每条路径的黑色节点个数
		int RefVal = 0;//定一个标准
		
		Node* cur = _root;
		while (cur)//改了一个bug ,之前这儿脑子抽抽了,写成了if
		{
			if (cur->_col == BLACK)
			{
				++RefVal;
			}
			cur = cur->_left;
		}
		return Check(_root, blacknum,RefVal);
	}

private:
	Node* _root = nullptr;
};

6. 红黑树的验证

红黑树的检测分为两步:

1. 检测其是否满足二叉搜索树(中序遍历是否为有序序列)

#include <vector>
#include "RBTree.h"

int main()
{
	int arr[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
	RBTree<int, int> a;
	for (auto e : arr)
	{
		a.insert(make_pair(e, e));
	}
	a.InOrder();
	return 0;
}

在这里插入图片描述


2. 检测其是否满足红黑树的性质

我们可以通过递归地遍历红黑树的节点,并统计根节点到每个叶子节点路径上的黑色节点个数来进行检查。

//根节点到当前这条路径的黑色节点个数
bool Check(Node *root,int blacknum,const int RefVal)
{
	if (root == nullptr)
	{
		if (blacknum != RefVal)
		{
			return false;
		}
		return true;
	}
	//直接反向检查,儿子很复杂,父亲只有一个
	if (root->_col == RED && root->_parent->_col == RED)
	{
		cout<<"有连续的红节点" << endl;
		return false;
	}
	if (root->_col == BLACK)
	{
		blacknum++;
	}
	return Check(root->_left, blacknum, RefVal) && Check(root->_right, blacknum, RefVal);
}

bool IsBalance()
{
	if (_root == nullptr)
	{
		return true;
	}
	if (_root->_col == RED)
	{
		return false;
	}


	int blacknum = 0;//记录每条路径的黑色节点个数
	int RefVal = 0;//定一个标准
	
	Node* cur = _root;
	while (cur)//改了一个bug ,之前这儿脑子抽抽了,写成了if
	{
		if (cur->_col == BLACK)
		{
			++RefVal;
		}
		cur = cur->_left;
	}
	return Check(_root, blacknum,RefVal);
}

函数讲解

Check 函数是一个递归函数,用于检查从当前节点到叶子节点的路径上的黑色节点个数是否与参考值 RefVal 相等。参数 root 表示当前节点指针,blacknum 表示根节点到当前节点的路径上的黑色节点个数,RefVal 是参考值。

  • 如果当前节点为空指针,即到达了叶子节点,那么检查路径上的黑色节点个数 blacknum 是否等于参考值 RefVal,如果不相等则返回 false,否则返回 true。
  • 如果当前节点的颜色为红色,并且父节点也是红色,表示存在连续的红节点,不符合红黑树的性质,返回 false。
  • 如果当前节点的颜色为黑色,将 blacknum 值加一。
  • 递归地调用 Check 函数检查左子节点和右子节点,并将当前节点的黑色节点个数 blacknum 作为参数传递。

IsBalance 函数用于检查整个红黑树是否符合红黑树的性质。

  • 如果红黑树为空树,即根节点为空,认为是一棵合法的红黑树,返回 true。
  • 如果根节点的颜色是红色,违反了红黑树的性质,返回 false。
  • 初始化 blacknum 为 0,用于记录每条路径的黑色节点个数。
  • 初始化 RefVal 为 0,作为参考值。
  • 通过遍历从根节点到最左子节点的路径,统计参考值 RefVal,即路径上的黑色节点个数。
  • 调用 Check 函数,传入根节点、路径上的黑色节点个数 blacknum 和参考值 RefVal 进行检查。
#include <vector>
#include "RBTree.h"
int main()
{
	const int N = 30;
	vector<int> v;
	v.reserve(N);
	srand(time(0));

	for (size_t i = 0; i < N; i++)
	{
		v.push_back(rand()%100 );
	}
	RBTree<int, int> t;
	for (auto e : v)
	{
		if (e == 0)
		{
			int i = 0;
		}
		t.insert(make_pair(e, e));
		cout << "Insert" << e << "->" << t.IsBalance() << endl;
	}
	t.InOrder();//中序打印
	if (t.IsBalance())
	{
		cout << "是平衡二叉树" << endl;
	}
	else
	{
		cout << "不是平衡二叉树" << endl;
	}
	return 0;
}

在这里插入图片描述

7. 红黑树与AVL树的比较

红黑树和AVL树都是高效的平衡二叉树,增删改查的时间复杂度都是O( l o g 2 N log_2 N log2N),红黑树不追求绝对平衡,其只需保证最长路径不超过最短路径的2倍,相对而言,降低了插入和旋转的次数,所以在经常进行增删的结构中性能比AVL树更优,而且红黑树实现比较简单,所以实际运用中红黑树更多。

8. 红黑树的应用

  1. C++ STL库 – map/set、mutil_map/mutil_set
    C++ STL中的std::map和std::set:这些容器类通常使用红黑树作为底层数据结构,提供了高效的查找、插入和删除操作。

  2. 数据库系统:红黑树常被用作数据库索引结构,例如在关系型数据库中,可以使用红黑树来实现B+树索引,提供高效的数据检索和排序功能。

  3. 线程调度器:操作系统中的线程调度器通常需要高效地管理和调度线程,红黑树可用于实现定时器和任务调度器,以快速查找和处理就绪的线程。

  4. 路由算法:在网络路由算法中,红黑树可以用于快速查找最佳路径和路由表项,提供高效的路由查找和转发功能。

  5. linux内核
    文件系统:某些文件系统使用红黑树来管理文件和目录的索引,以支持快速的文件查找和访问。

  6. 编译器和解释器:在编译器和解释器中,红黑树可以用于符号表的实现,以支持快速的符号查找和关联。

  7. 并发数据结构:在多线程和并发编程中,红黑树可以用于实现并发安全的数据结构,例如并发哈希表、并发有序集合等。

(本章完)

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C_6微机原理

一、单项选择题&#xff08;本大题共 15小题&#xff0c;每小题3分&#xff0c;共45分。在每小题给出的四个备选项中&#xff0c;选出一个正确的答案&#xff0c;请将选定的答案填涂在答题纸的相应位置上。 n1 位有符号数 的补码表示范围为&#xff08;&#xff09; A. -2n&l…

Mybatis 源码搭建

文章目录 源码下载测试模块搭建学习博客 源码下载 首先下载mybatis-parent的源码&#xff1a;gitee地址 > https://gitee.com/callback_lab/mybatis-parent.git 然后下载mybatis的源码&#xff1a;gitee地址 > https://gitee.com/callback_lab/mybatis-src.git 带中文…

SpringCloud之Gateway(统一网关)

文章目录 前言一、搭建网关服务1、导入依赖2、在application.yml中写配置 二、路由断言工厂Route Predicate Factory三、路由过滤器 GatewayFilter案例1给所有进入userservice的请求添加一个请求头总结 四、全局过滤器 GlobalFilter定义全局过滤器&#xff0c;拦截并判断用户身…

瑞数五代ast反混淆笔记一

第一部分 瑞数五代ast反混淆笔记一 文章目录 前言一、分析第一层js文件二、转换为switch-case三、效果图总结 前言 瑞数五代的反混淆做了很久了&#xff0c;当时写的很复杂&#xff0c;也没有记笔记&#xff0c;现在看代码都看不懂了&#xff0c;重新归顺下逻辑思路 一、分析第…

python+pytest接口自动化(2)-HTTP协议基础

HTTP协议简介 HTTP 即 HyperText Transfer Protocol&#xff08;超文本传输协议&#xff09;&#xff0c;是互联网上应用最为广泛的一种网络协议。所有的 WWW 文件都必须遵守这个标准。 设计 HTTP 最初的目的是为了提供一种发布和接收 HTML 页面的方法。HTTP 协议在 OSI 模型…

C#-创建用于测试的父类StartupBase用于服务注入

当写完C#代码&#xff0c;需要对某个方法进行测试。 创建一个XXXTests.cs文件之后&#xff0c;发现需要注入某个服务怎么办&#xff1f; 再创建一个StartupBase.cs文件&#xff1a; public abstract class StartupBase {public IConfiguration Configuration { get; }public …

MATLAB中FFT频谱分析使用详解

文章目录 语法说明语法一&#xff1a;Y fft(X)fft(X)返回X长度的傅里叶变换 语法二&#xff1a;Y fft(X,N)如果 X的长度小于 N&#xff0c;则为 X补上尾零以达到长度 N(FFT插值)双边谱转换为单边谱 如果 X 的长度大于 N&#xff0c;则对 X 进行截断以达到长度 N。 语法三&…

14. 最长公共前缀

这篇文章会收录到 : 算法通关村第十二关-黄金挑战字符串冲刺题-CSDN博客 14. 最长公共前缀 描述 : 编写一个函数来查找字符串数组中的最长公共前缀。 如果不存在公共前缀&#xff0c;返回空字符串 ""。 题目 : LeetCode 14.最长公共前缀 : 14. 最长公共前缀 分…

在OpenCV中基于深度学习的边缘检测

引言 如何在OpenCV中使用基于深度学习的边缘检测&#xff0c;它比目前流行的canny边缘检测器更精确。边缘检测在许多用例中是有用的&#xff0c;如视觉显著性检测&#xff0c;目标检测&#xff0c;跟踪和运动分析&#xff0c;结构从运动&#xff0c;3D重建&#xff0c;自动驾驶…

多功能音乐沙漏的设计与实现

【摘要】随着当今社会快节奏生活的发展&#xff0c;当代大学生越来忽视时间管理的重要性&#xff0c;在原本计划只看几个视频只玩几个游戏的碎片化娱乐中耗费了大量的时光&#xff0c;对于自己原本的学习生活产生了巨大的影响。为更加有效的反映时间的流逝&#xff0c;特设计该…

解决VMware VCenter存储上传镜像文件失败

VMware VCSA6.7上传共享文件时提示操作失败&#xff0c;由于不确定的原因&#xff0c;操作失败。通常&#xff0c;当浏览器不信任证书时会发生此问题。如果您使用的是自签名证书或自定义证书&#xff0c;请在新的浏览器选项卡中打开下面的 URL并接受证书&#xff0c;然后重试操…

室内定位(WiFi/UWB/蓝牙等)技术方案概述

室内无法搜索到卫星&#xff0c;这样常规的GPS/北斗定位都无法使用&#xff0c;常规免费的只有运营商的基站定位LBS&#xff0c;但这个精度实在太差&#xff0c;一般都有几十米到几百米的偏差。因此&#xff0c;室内定位一直是个老大难问题。 截至目前&#xff0c;业界比较成熟…

深度学习黎明时期的LeNet:揭开卷积神经网络的序幕

在深度学习的历史长河中&#xff0c;Yann LeCun 的 LeNet 是一个里程碑式的研究成果&#xff0c;它为后来的卷积神经网络&#xff08;Convolutional Neural Networks&#xff0c;CNN&#xff09;的发展奠定了基础。LeNet 的诞生标志着深度学习黎明时期的到来&#xff0c;为人工…

在Linux中部署MeterSphere并且结合内网穿透实现远程访问本地管理页面——“cpolar内网穿透”

文章目录 前言1. 安装MeterSphere2. 本地访问MeterSphere3. 安装 cpolar内网穿透软件4. 配置MeterSphere公网访问地址5. 公网远程访问MeterSphere6. 固定MeterSphere公网地址 前言 MeterSphere 是一站式开源持续测试平台, 涵盖测试跟踪、接口测试、UI 测试和性能测试等功能&am…

VMware虚拟机网络配置详解

vmware为我们提供了三种网络工作模式&#xff0c;它们分别是&#xff1a;Bridged&#xff08;桥接模式&#xff09;、NAT&#xff08;网络地址转换模式&#xff09;、Host-Only&#xff08;仅主机模式&#xff09; 打开vmware虚拟机&#xff0c;我们可以在选项栏的“编辑”下的…

基于51单片机的超声波测距系统【程序+proteus仿真+参考论文+原理图+PCB等16个文件夹资料】

一、项目功能简介 整个设计系统由STC89C52单片机LCD1602显示模块声光报警模块存储模块超声波模块按键模块组成。 具体功能&#xff1a; 1、超声波测量距离&#xff0c;显示在LCD1602。 2、存储模块可以存储超声波报警值。 3、通过按键可设置报警值大小。 4、超声波报警距…

迁移redis数据库中的数据到另一台服务器

方案一 下面我使用的redis是用docker安装的&#xff0c;不是通过下载安装包安装的&#xff0c;所以和我安装方式不一样的小伙伴可以不看&#xff0c;因为很多操作是基于docker的 话不多说&#xff0c;直接开搞&#xff01; 1.首先一定要确保两台服务器上面的redis版本要一致…

无人机遥控器方案定制_MTK平台无人设备手持遥控终端PCB板开发

随着科技的不断发展和无人机技术的逐步成熟&#xff0c;无人机越来越受到人们的关注。作为一种高新技术&#xff0c;无人机的应用范围不断拓展&#xff0c;包括农业、环境监测、城市规划、运输物流等领域。同时&#xff0c;无人机的飞行控制技术也得到了不断的优化和提升。 早…

[激光器原理与应用-15]:声光调制器(AOM:Acousto-optic modulator)

目录 第1章 概述 1.1 什么是AOM 1.2 AOM的主要参数 第2章 主要工作原理 2.1 光的调制技术 2.2 直接调制与间接调制 2.3 声光调制 2.4 声光调制工作原理 第3章 声光调制器件 3.1 声光调制器件的类型 3.2 应用 3.3 主要厂家 第4章 声光调制器系统 4.1 系统组成 …

Java(八)(可变参数,Collections,小案例:斗地主游戏小案例:斗地主游戏,Map集合,Stream流)

目录 可变参数 Collections 小案例:斗地主游戏 Map集合 Map的常用方法 map集合的遍历 键找值 键值对 Lambda 表达式 HashMap底层原理 集合的嵌套 Stream流 获取集合或数组的Stream流 Stream流的方法 可变参数 就是一种特殊的形参,定义在方法和构造器的形参列表中,…