【C++篇】红黑树的实现

目录

前言:

一,红黑树的概念

1.1,红黑树的规则

1.2,红黑树的最长路径 

1.3,红黑树的效率分析

 二,红黑树的实现

2.1,红黑树的结构

2.2,红黑树的插入

2.2.1,大致过程 

2.2.2,情况1:变色处理 

 2.2.3,情况2:单旋+变色

2.2.4,情况3:双旋+变色

2.3,红黑树插入代码的实现

2.4,红黑树的验证

三,整体代码

四,测试代码


前言:

本篇会用到上篇【AVL树的实现】中的旋转知识。

一,红黑树的概念

        红黑树是一颗二叉搜索树,它的每一个节点增加一个存储为来表示节点的颜色。可以是红色或者黑色。它通过对从根开始到叶子节点的每条路径上各个节点颜色的约束,确保最长路径不会超过最短路径的2倍,从而实现平衡的。

1.1,红黑树的规则

1,每个节点不是红色就是黑色。

2,根节点是黑色的。

3,红色节点的两个孩子只能是 黑色节点或者是空节点。也就是说不能出现连续的红色节点

4,对于任意一个节点,从该节点开始,到叶子节点的所有路径上,均包含相同数量的黑色节点。

以上 都是红黑树,满足红黑树的规则。

1.2,红黑树的最长路径 

1,由第四条规则可知,从根节点开始的每条路径上,黑色节点的数量相同。所以在极端场景下,一颗红黑树,它的最短路径就是节点全为黑色的路径。假设红黑树的每条路径黑色节点数量都为b,那么最短路径的节点数量为b.

2,由 第三条规则可知,一条路径上不能由连续的红色节点,最长路径是由一黑一红间隔组成的,所以最长路径为2*b。

3,而对于一颗红黑树,最长和最短路径不一定存在。我们可以得出对于任意一颗红黑树,它的任意 一条路径长度x都是,b<=x<=2*b.

1.3,红黑树的效率分析

假设N是红黑树节点的数量,h是最短路径的长度,最长路径不超过2*h

可以得到2^h-1<= N <= 2^(2*h)-1,推出h大致为logN,也就意味着红黑树的增删查该最坏走2*logN,时间复杂度O(logN).

 二,红黑树的实现

2.1,红黑树的结构

enum color
{
    Red,
    Black
};

template<class k,class v>
struct RBTreeNode
{
    RBTreeNode(const pair<k,v>& kv)
        :_left(nullptr)
        ,_right(nullptr)
        ,_parent(nullptr)
        ,_kv(kv)
    {}
    RBTreeNode<k, v>* _left;
    RBTreeNode<k, v>* _right;
    RBTreeNode<k, v>* _parent;
    pair<k, v> _kv;
    color _col;
};

template<class k,class v>
class RBTree
{
    typedef RBTreeNode<k, v> Node;
public:

   //...

private:

    Node* _root=nullptr;
};
 

2.2,红黑树的插入

2.2.1,大致过程 

1,插入一个值需要按照搜索树的规则进行插入,再判断插入后是否满足红黑树的规则。

2,如果是空树插入,新增节点就是黑色节点。如果是非空树插入,新增节点就必须是红色节点,因为如果插入黑色节点,就一定会破坏规则4,而插入红色节点是有可能会破坏规则3,而且对于规则3来说,解决方法比规则4的解决方法容易。

3,非空树插入后,如果父节点是黑色节点,则没有违反任何规则,插入结束。

4,非空树插入后,如果父节点是红色节点,则违反规则3,进一步分析。

2.2.2,情况1:变色处理 

由上图可知,c为红,p为红,g为黑,u存在且为红。在这种情况下,我们需要将p和u变黑,g变红,g成为新的c,继续向上更新。

分析:

        因为p和u都是红色的,g是黑色的。把p和u变黑,左边子树路径各增加一个黑色节点,g再变红,相当于g所在路径的黑色节点数量不变,同时解决了c和p连续红节点的问题。

        需要继续往上跟新是因为g是红色,如果g的父亲还是红色,就需要继续处理;如果g的父亲是黑色,则处理结束;如果g是整棵树的根,再将g变成黑色即可。 

 2.2.3,情况2:单旋+变色

c为红,p为红,u不存在或者u存在且u为黑色。在这种情况下,就需要进行单旋+变色处理

分析:

u不存在时,c一定是新增节点。

u存在且为黑色时,c一定不是新增节点,是在c的子树中插入,符号情况1,经过情况1后,c被调整为红色的。

 上图展示的是右单旋的场景,下面是根据右单旋,画出的左单旋大致图,与右单旋过程相似:

 总结:经过单旋+变色后,我们可以看到p做了子树新的根,且p是黑色的,所以不管p的父亲是黑色的还是红色的,都满足红黑树的规则,所以此时,就不需要往上更新了。

2.2.4,情况3:双旋+变色

c为红,p为红,g为黑,u不存在或者u存在且为黑,u不存在,则c一定是新增结点,u存在且为黑,则 c一定不是新增,c之前是黑色的,是在c的子树中插入,符合情况1,变色将c从黑色变成红色,更新上来的。

 上图展示的是左右双旋+变色,同样右左双旋类似:

同样经过双旋+变色后,c成为新的根,且c为黑色,所以也不需要继续向上更新了。

2.3,红黑树插入代码的实现

bool Insert(const pair<k, v>& kv)
{
	//插入根节点,color->Black
	if (_root == nullptr)
	{
		_root = new Node(kv);
		_root->_col = Black;
		return true;
	}
	//根据搜索树的规则查找插入位置
	Node* cur = _root;
	Node* parent = nullptr;
	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;
	else
		parent->_left = cur;
	cur->_parent = parent;

	//颜色处理+旋转
	while (parent&& parent->_col == Red)
	{
		//g
		Node* grandfather = parent->_parent;
		if (parent == grandfather->_left)
		{
			//p为g的左边
			//    g
			//  p   u
			Node* uncle = grandfather->_right;
			//叔叔存在且为红,情况1
			if (uncle && uncle->_col == Red)
			{
				//变色
				parent->_col = Black;
				uncle->_col = Black;
				grandfather->_col = Red;
				//继续向上处理
				cur = grandfather;
				parent = cur->_parent;
			}
			else
			{
				//情况2,3
				//叔叔不存在或者叔叔为黑
				//u为黑,则c之前是黑的
				//u不存在,则c是新插入的
				if (cur == parent->_left)
				{
					//右单旋场景
					 //   g
			        //  p   u
		           // c
					RotateR(grandfather);
					//变色
					parent->_col = Black;
					grandfather->_col = Red;
				}
				else
				{
					//左右双旋场景
					//    g
					//  p   u
				   //     c
					RotateL(parent);
					RotateR(grandfather);
					//变色
					cur->_col = Black;
					grandfather->_col = Red;
				}
				//不需要进行向上更新
				break;
			}
		}
		else
		{
			//p为g的右边
			//   g
			// u   p
			Node* uncle = grandfather->_left;
			if (uncle && uncle->_col == Red)
			{
				//情况1
				//变色
				parent->_col = Black;
				uncle->_col = Black;
				grandfather->_col = Red;

				cur = grandfather;
				parent = cur->_parent;
			}
			else
			{
				//情况2,3
				if (cur == parent->_right)
				{
					//左单旋场景
					   //  g
			          // u   p
				     //       c
					RotateL(grandfather);
					//变色
					parent->_col = Black;
					grandfather->_col = Red;
				}
				else
				{
					//右左双旋场景
					//   g
				    // u   p
				    //   c
					RotateR(parent);
					RotateL(grandfather);
					//变色
					cur->_col = Black;
					grandfather->_col = Red;
				}
				//不需要继续向上更新
				break;
			}
		}
	}
	//跟的颜色可能被调整为红色,最后一步改为黑色即可
	_root->_col = Black;
	return true;
}
//右单旋
void RotateR(Node* parent)
{
	Node* subL = parent->_left;
	Node* subLR = subL->_right;
	Node* pparent = parent->_parent;

	if (subLR)
		subLR->_parent = parent;
	parent->_left = subLR;
	subL->_right = parent;
	parent->_parent = subL;
	if (parent == _root)
	{
		_root = subL;
		_root->_parent = nullptr;
	}
	else
	{
		if (pparent->_left == parent)
			pparent->_left = subL;
		else
			pparent->_right = subL;
		subL->_parent = pparent;
	}
}
//左单旋
void RotateL(Node* parent)
{
	Node* subR = parent->_right;
	Node* subRL = subR->_left;
	Node* pparent = parent->_parent;

	parent->_right = subRL;
	if (subRL)
		subRL->_parent = parent;
	parent->_parent = subR;
	subR->_left = parent;
	if (parent == _root)
	{
		_root = subR;
		_root->_parent = nullptr;
	}
	else
	{
		if (pparent->_left == parent)
			pparent->_left = subR;
		else
			pparent->_right = subR;
		subR->_parent = pparent;
	}
}

2.4,红黑树的验证

我们只需验证形成的树是否满足红黑树的四条规则即可。

其中,规则1和规则 2可以直接检查。

对于规则3,我们可以进行前序遍历,遍历到一个节点,判断该节点的颜色,再判断它的两个孩子的颜色,这样做太麻烦了。我们可以反过来,遍历到一个节点,如果他是红色的,判断它的父亲节点是否为黑色。

对于规则4,我们可以先从根开始找到一条路径上黑色节点的个数refNum,再对整棵树进行前序遍历,用变量 blackNum记录黑色节点的个数,当遍历到空的时候,与refNum比较即可。

bool Check(Node* root, int BlackNum, int num)
{
	if (root == nullptr)
	{
		if (BlackNum != num)
			return false;
		return true;
	}

	if (root->_col == Red && root->_parent->_col == Red)
		return false;
	if (root->_col == Black)
		BlackNum++;
	return Check(root->_left, BlackNum, num) && Check(root->_right, BlackNum, num);
}
//验证
bool isbalance()
{
	if (_root == nullptr)
		return true;
	if (_root->_col == Red)
		return false;
	int num = 0;
	Node* cur = _root;
	while (cur)
	{
		if (cur->_col == Black)
			num++;
		cur = cur->_left;
	}
	return Check(_root, 0, num);
}

三,整体代码

enum color
{
	Red,
	Black
};

template<class k,class v>
struct RBTreeNode
{
	RBTreeNode(const pair<k,v>& kv)
		:_left(nullptr)
		,_right(nullptr)
		,_parent(nullptr)
		,_kv(kv)
	{}
	RBTreeNode<k, v>* _left;
	RBTreeNode<k, v>* _right;
	RBTreeNode<k, v>* _parent;
	pair<k, v> _kv;
	color _col;
};

template<class k,class v>
class RBTree
{
	typedef RBTreeNode<k, v> Node;
public:
	bool Insert(const pair<k, v>& kv)
	{
		//插入根节点,color->Black
		if (_root == nullptr)
		{
			_root = new Node(kv);
			_root->_col = Black;
			return true;
		}
		//根据搜索树的规则查找插入位置
		Node* cur = _root;
		Node* parent = nullptr;
		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;
		else
			parent->_left = cur;
		cur->_parent = parent;

		//颜色处理+旋转
		while (parent&& parent->_col == Red)
		{
			//g
			Node* grandfather = parent->_parent;
			if (parent == grandfather->_left)
			{
				//p为g的左边
				//    g
				//  p   u
				Node* uncle = grandfather->_right;
				//叔叔存在且为红,情况1
				if (uncle && uncle->_col == Red)
				{
					//变色
					parent->_col = Black;
					uncle->_col = Black;
					grandfather->_col = Red;
					//继续向上处理
					cur = grandfather;
					parent = cur->_parent;
				}
				else
				{
					//情况2,3
					//叔叔不存在或者叔叔为黑
					//u为黑,则c之前是黑的
					//u不存在,则c是新插入的
					if (cur == parent->_left)
					{
						//右单旋场景
						 //   g
				        //  p   u
			           // c
						RotateR(grandfather);
						//变色
						parent->_col = Black;
						grandfather->_col = Red;
					}
					else
					{
						//左右双旋场景
						//    g
						//  p   u
					   //     c
						RotateL(parent);
						RotateR(grandfather);
						//变色
						cur->_col = Black;
						grandfather->_col = Red;
					}
					//不需要进行向上更新
					break;
				}
			}
			else
			{
				//p为g的右边
				//   g
				// u   p
				Node* uncle = grandfather->_left;
				if (uncle && uncle->_col == Red)
				{
					//情况1
					//变色
					parent->_col = Black;
					uncle->_col = Black;
					grandfather->_col = Red;

					cur = grandfather;
					parent = cur->_parent;
				}
				else
				{
					//情况2,3
					if (cur == parent->_right)
					{
						//左单旋场景
						   //  g
				          // u   p
					     //       c
						RotateL(grandfather);
						//变色
						parent->_col = Black;
						grandfather->_col = Red;
					}
					else
					{
						//右左双旋场景
						//   g
					    // u   p
					    //   c
						RotateR(parent);
						RotateL(grandfather);
						//变色
						cur->_col = Black;
						grandfather->_col = Red;
					}
					//不需要继续向上更新
					break;
				}
			}
		}
		//跟的颜色可能被调整为红色,最后一步改为黑色即可
		_root->_col = Black;
		return true;
	}
	//右单旋
	void RotateR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;
		Node* pparent = parent->_parent;

		if (subLR)
			subLR->_parent = parent;
		parent->_left = subLR;
		subL->_right = parent;
		parent->_parent = subL;
		if (parent == _root)
		{
			_root = subL;
			_root->_parent = nullptr;
		}
		else
		{
			if (pparent->_left == parent)
				pparent->_left = subL;
			else
				pparent->_right = subL;
			subL->_parent = pparent;
		}
	}
	//左单旋
	void RotateL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;
		Node* pparent = parent->_parent;

		parent->_right = subRL;
		if (subRL)
			subRL->_parent = parent;
		parent->_parent = subR;
		subR->_left = parent;
		if (parent == _root)
		{
			_root = subR;
			_root->_parent = nullptr;
		}
		else
		{
			if (pparent->_left == parent)
				pparent->_left = subR;
			else
				pparent->_right = subR;
			subR->_parent = pparent;
		}
	}
	void Inorder()
	{
		_Inorder(_root);
	}
	int Height()
	{
		return _Height(_root);
	}
	int size()
	{
		return _size(_root);
	}

	Node* Find(const k& key)
	{
		Node* cur = _root;
		while (cur)
		{
			if (cur->_kv.first < key)
			{
				cur = cur->_right;
			}
			else if (cur->_kv.first > key)
			{
				cur = cur->_left;
			}
			else
			{
				return cur;
			}
		}
		return nullptr;
	}
	//验证
	bool isbalance()
	{
		if (_root == nullptr)
			return true;
		if (_root->_col == Red)
			return false;
		int num = 0;
		Node* cur = _root;
		while (cur)
		{
			if (cur->_col == Black)
				num++;
			cur = cur->_left;
		}
		return Check(_root, 0, num);
	}
private:
	bool Check(Node* root, int BlackNum, int num)
	{
		if (root == nullptr)
		{
			if (BlackNum != num)
				return false;
			return true;
		}

		if (root->_col == Red && root->_parent->_col == Red)
			return false;
		if (root->_col == Black)
			BlackNum++;
		return Check(root->_left, BlackNum, num) && Check(root->_right, BlackNum, num);
	}
	int _size(Node* root)
	{
		if (root == nullptr)
			return 0;
		return _size(root->_left) + _size(root->_right) + 1;
	}
	int _Height(Node* root)
	{
		if (root == nullptr)
			return 0;
		int leftHeight = _Height(root->_left);
		int rightHeight = _Height(root->_right);
		return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
	}
	void _Inorder(Node* root)
	{
		if (root == nullptr)
			return;
		_Inorder(root->_left);
		cout << root->_kv.first << ":" << root->_kv.second << endl;
		_Inorder(root->_right);
	}
	Node* _root=nullptr;
};

四,测试代码

void TestRBTree1()
{
	RBTree<int, int> t;
	// 常规的测试用例
	int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
	// 特殊的带有双旋场景的测试用例
	//int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };

	for (auto e : a)
	{
		t.Insert({ e, e });
	}

	t.Inorder();
	cout << t.isbalance() << endl;
}
int main()
{
	TestRBTree1();
	return 0;
}

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

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

相关文章

【MySQL】使用C语言链接

&#x1f308; 个人主页&#xff1a;Zfox_ &#x1f525; 系列专栏&#xff1a;MySQL 目录 一&#xff1a;&#x1f525; MySQL connect &#x1f98b; Connector / C 使用&#x1f98b; mysql 接口介绍&#x1f98b; 完整代码样例 二&#xff1a;&#x1f525; 共勉 一&#…

音视频入门基础:RTP专题(4)——FFmpeg源码中,判断某文件是否为SDP文件的实现

一、引言 执行《音视频入门基础&#xff1a;RTP专题&#xff08;2&#xff09;——使用FFmpeg命令生成RTP流》中的“媒体文件转推RTP的FFmpeg命令”会生成一个SDP文件&#xff0c;该文件内容如下&#xff1a; v0 o- 0 0 IN IP4 127.0.0.1 sNo Name t0 0 atool:libavformat 61…

SSM项目简单的增删改查

目录 一、表 二、创建项目 1.创建mavenJavaWeb项目 2.补齐目录 3.导入依赖 三、创建包结构 四、实体类 五、spring框架 1.service接口和实现类 (1)service接口 (2)实现类 2.applicationContext.xml配置文件 六、spring整合springMVC 1.web.xml 2.spring-mvc.xml …

【Vim Masterclass 笔记13】第 7 章:Vim 核心操作之——文本对象与宏操作 + S07L28:Vim 文本对象

文章目录 Section 7&#xff1a;Text Objects and MacrosS07L28 Text Objects1 文本对象的含义2 操作文本对象的基本语法3 操作光标所在的整个单词4 删除光标所在的整个句子5 操作光标所在的整个段落6 删除光标所在的中括号内的文本7 删除光标所在的小括号内的文本8 操作尖括号…

el-table多级表头和列单元格合并

1、表格结构 <el-table:data"dialogForm.tableData"stripe:border"true":span-method"arraySpanMethod"><!-- 日期列 --><el-table-column prop"time" label"日期" align"center" /><!-- 重…

工程水印相机结合图纸,真实现场时间地点,如何使用水印相机,超简单方法只教一次!

在工程管理领域&#xff0c;精准记录现场信息至关重要。水印相机拍照功能&#xff0c;为工程人员提供了强大的现场信息记录工具&#xff0c;助力工程管理和统计工程量&#xff0c;更可以将图片分享到电脑、分享给同事&#xff0c;协同工作。 一、打开图纸 打开手机版CAD快速看图…

uniApp开通uniPush1.0个推,SpringBoot集成uniPush1.0个推

uniApp开通unipush1.0个推&#xff0c;SpringBoot程序集成 一、APP开通unipush1.0个推(商户App源码仅支持1.0个推) 1.app模块配置开通推送 2.应用开通推送 3.开通后点击消息推送菜单会看到如下页面 完成以上步骤后 此时android 仅支持在线推送。 4.配置各厂商离线推送 暂未…

升级 SpringBoot3 全项目讲解 — 为什么 SpringBoot3 应该抛弃 Maven,搭配 Gradle 来使用?

学会这款 &#x1f525;全新设计的 Java 脚手架 &#xff0c;从此面试不再怕&#xff01; 随着 Spring Boot 3 的发布&#xff0c;许多开发者开始考虑如何将现有项目升级到最新版本。Spring Boot 3 带来了许多新特性&#xff0c;包括对 Java 17 的支持、更好的性能优化以及对 G…

Yolov8 目标检测剪枝学习记录

最近在进行YOLOv8系列的轻量化&#xff0c;目前在网络结构方面的优化已经接近极限了&#xff0c;所以想要学习一下模型剪枝是否能够进一步优化模型的性能 这里主要参考了torch-pruning的基本使用&#xff0c;v8模型剪枝&#xff0c;Jetson nano部署剪枝YOLOv8 下面只是记录一个…

【深度学习】关键技术-激活函数(Activation Functions)

激活函数&#xff08;Activation Functions&#xff09; 激活函数是神经网络的重要组成部分&#xff0c;它的作用是将神经元的输入信号映射到输出信号&#xff0c;同时引入非线性特性&#xff0c;使神经网络能够处理复杂问题。以下是常见激活函数的种类、公式、图形特点及其应…

图数据库 | 18、高可用分布式设计(中)

上文我们聊了在设计高性能、高可用图数据库的时候&#xff0c;从单实例、单节点出发&#xff0c;一般有3种架构演进选项&#xff1a;主备高可用&#xff0c;今天我们具体讲讲分布式共识&#xff0c;以及大规模水平分布式。 主备高可用、分布式共识、大规模水平分布式&#xff…

Oracle 终止正在执行的SQL

目录 一. 背景二. 操作简介三. 投入数据四. 效果展示 一. 背景 项目中要求进行性能测试&#xff0c;需要向指定的表中投入几百万条数据。 在数据投入的过程中发现投入的数据不对&#xff0c;需要紧急停止SQL的执行。 二. 操作简介 &#x1f449;需要DBA权限&#x1f448; ⏹…

Datawhale组队学习笔记task1——leetcode面试题

文章目录 写在前面刷题流程刷题技巧 Day1题目1、0003.无重复字符的最长子串解答&#xff1a;2.00004 寻找两个正序数组的中位数解答&#xff1a;3.0005.最长回文子串解答4.0008.字符串转换整数解答&#xff1a; Day2题目1.0151.反转字符串中的单词解答2.0043.字符串相乘解答3.0…

K3二开:在工业老单工具栏增加按钮,实现打印锐浪报表

在上次实现用GridRepot报表实现打印任务单后&#xff0c;在想着能不能给将生产任务单原来要通过点击菜单栏&#xff0c;打印任务单的功能&#xff0c;在工具栏上也增加按钮实现&#xff0c;这样就不需要多点了。 原本是需要点击菜单栏才能实现的 现在在工具栏上增加按钮实现同…

[计算机网络]一. 计算机网络概论第一部分

作者申明&#xff1a;作者所有文章借助了各个渠道的图片视频以及资料&#xff0c;在此致谢。作者所有文章不用于盈利&#xff0c;只是用于个人学习。 1.0推荐动画 【网络】半小时看懂<计算机网络>_哔哩哔哩_bilibili 1.1计算机网络在信息时代的作用 在当今信息时代&…

机器学习之支持向量机SVM及测试

目录 1 支持向量机SVM1.1 概念1.2 基本概念1.3 主要特点1.4 优缺点1.5 核函数1.6 常用的核函数1.7 函数导入1.8 函数参数 2 实际测试2.1 二维可视化测试代码2.2 多维测试 1 支持向量机SVM 1.1 概念 支持向量机&#xff08;Support Vector Machine&#xff0c;简称SVM&#xff…

云服务信息安全管理体系认证,守护云端安全

在数据驱动的时代&#xff0c;云计算已成为企业业务的超级引擎&#xff0c;推动着企业飞速发展。然而&#xff0c;随着云计算的广泛应用&#xff0c;信息安全问题也日益凸显&#xff0c;如同暗流涌动下的礁石&#xff0c;时刻威胁着企业的航行安全。这时&#xff0c;云服务信息…

服务器数据恢复—Zfs文件系统数据恢复案例

服务器数据恢复环境&故障&#xff1a; 一台zfs文件系统的服务器&#xff0c;管理员误操作删除了服务器上的数据。 服务器数据恢复过程&#xff1a; 1、将故障服务器中所有硬盘做好标记后取出&#xff0c;硬件工程师检测后没有发现有硬盘存在硬件故障。以只读方式将所有硬盘…

​​​​​​​​​​​​​​★3.3 事件处理

★3.3.1 ※MouseArea Item <-- MouseArea 属性 acceptedButtons : Qt::MouseButtons containsMouse : bool 【书】只读属性。表明当前鼠标光标是否在MouseArea上&#xff0c;默认只有鼠标的一个按钮处于按下状态时才可以被检测到。 containsPress : bool curs…

GIS大模型:三维重建与建模

文章目录 数据收集预处理特征提取深度估计点云生成表面重建纹理映射大模型的角色 大模型在三维重建与建模方面&#xff0c;尤其是在处理低空地图数据时&#xff0c;展现了其强大的能力。通过使用深度学习算法&#xff0c;特别是那些基于卷积神经网络&#xff08;CNNs&#xff0…