010 数据结构_红黑树

news2024/11/18 11:36:47

前言

本文将会向你介绍红黑树的概念、性质,以及如何手撕红黑树

1 文章重点

文本首先引入红黑树的概念和性质,性质非常重要对于后面的插入操作来说,文章的核心放在了插入部分,另外看插入部分之前记得看声名和节点的定义哦~

2 引入红黑树

2.1概念

首先红黑树是一颗二叉搜索树,每个节点都有颜色,红色或黑色,最长路径最多是最短路径的二倍

2.2性质

1、 每个节点不是红色就是黑色。
2、 根部节点是黑色的。
3、 一条路径上不能出现连续的红色节点
4、每条路径都必须包含相同数量的黑色节点。

3 测试

	// 检测红黑树中是否存在值为data的节点,存在返回该节点的地址,否则返回nullptr
	Node* Find(const pair<K, V>& kv)
	{
		Node* cur = _root;
		Node* parent = _root;
		Node* grandParent = parent->_parent;
		//判断是否为空树
		if (_root == nullptr)
		{
			return nullptr;
		}
		//寻找插入位置
		else
		{
			Node* parent = cur;
			while (cur)
			{
				//记录父节点的位置,便于后续的链接操作
				parent = cur;
				//向左遍历
				if (cur->_kv.first > kv.first)
				{
					cur = cur->_left;
				}
				//向右遍历
				else if (cur->_kv.first < kv.first)
				{
					cur = cur->_right;
				}
				//已有
				else return cur;
			}
		}
	}

	//中序遍历,验证是否为二叉搜索树
	void InOrder()
	{
		_InOrder(_root);
		cout << endl;
	}
	void _InOrder(Node* root)
	{
		if (root == nullptr)
			return;

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

	size_t _Height(Node* root)
	{
		if (root == nullptr)
		{
			return 0;
		}
		int leftHeight = _Height(root->_pLeft);
		int rightHeight = _Height(root->_pRight);
		return (leftHeight > rightHeight) ? leftHeight + 1 : rightHeight + 1;
	}
	int Height()
	{
		return _Height(_root);
	}
	bool Check(Node* root, int blackNum, const int ref)
	{
		if (root == nullptr)  //到根部看看当前路径黑色节点和标准值是否一致
		{
			//cout << blackNum << endl;
			if (blackNum != ref)
			{
				cout << "存在黑色节点数量不相等的路径" << endl;
				return false;
			}

			return true;
		}

		// 检查子比较复杂,可以反过来去检查红节点父是否为黑色
			if (root->_cl == RED && root->_parent->_cl == RED)
			{
				cout << "有连续的红色节点" << endl;

				return false;
			}

		if (root->_cl == BLACK)
		{
			++blackNum;  //为黑节点加一
		}

		return Check(root->_left, blackNum, ref)
			&& Check(root->_right, blackNum, ref);
	}

	bool _IsBalance(Node* root)
	{
		if (root == nullptr)
		{
			return true;
		}
		if (root->_cl == RED)
		{
			return false;
		}
		int ref = 0;
		Node* cur = root;
		//计算一条路径的黑色节点标准值
		while (cur)
		{
			if (cur->_cl == BLACK)
			{
				ref++;
			}
			cur = cur->_left;
		}
		int blackNum = 0;
		return Check(root, blackNum, ref); 
	}
	bool IsBalance()
	{
		return _IsBalance(_root);
	}
	

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

IsBalance 函数用于检查整个红黑树是否符合红黑树的性质。
如果红黑树为空树,即根节点为空,认为是一棵合法的红黑树,返回 true。
如果根节点的颜色是红色,违反了红黑树的性质,返回 false。
初始化 blacknum 为 0,用于记录每条路径的黑色节点个数。 初始化 ref 为 0,作为参考值。
通过遍历从根节点到最左子节点的路径,统计参考值 ref,即路径上的黑色节点个数。 调用 Check
函数,传入根节点、路径上的黑色节点个数 blacknum 和参考值 RefVal 进行检查

插入一千个数据(未全列出)
在这里插入图片描述

4 插入(重点)

声名

为了方便叙述,将进行以下定义
cur©——当前节点
parent§——父节点
grandParent(g)——父节点的父节点
uncle(u)——父节点的兄弟节点

节点的定义

enum Color
{
	RED,
	BLACK
};
template<class K, class V>
struct RBTreeNode
{
	RBTreeNode* _left;
	RBTreeNode* _right;
	RBTreeNode* _parent;
	RBTreeNode* _pHead;
	Color _cl;
	pair<K, V> _kv;
template<class K, class V>
	RBTreeNode(const pair<K, V>& kv)
		: _left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		,_pHead(nullptr)
		, _cl(RED)
		, _kv(kv)
	{}
};

_left:指向左子节点的指针。
_right:指向右子节点的指针。
_parent:指向父节点的指针。
_kv:存储键值对的成员变量。这里使用了 pair<K, V> 类型来表示键值对,其中 K 是键的类型,V 是值的类型。
_cl:表示节点的颜色。这里使用枚举类型来表示,其中 RED 表示红色,BLACK 表示黑色,构造出来的节点默认为红色

插入

对于新插入节点,我们设置为红色,原因是红黑树每条路径都必须包含相同数量的黑色节点(性质4),新插入红节点不一定破坏红黑树的结构,新插入黑色节点一定不符合性质4而且很难调整(新增一条路径的黑色节点,其他路径也需要增加(会很麻烦))

bool Insert(const pair<K, V>& kv)
	{				
		//记录当前节点
		Node* cur = _root;
		//记录父节点
		Node* parent = _root;
		//判断是否为空树
		if (_root == nullptr)
		{
			//直接插入
			_root = new Node(kv);
			//插入成功
			_root->_cl = BLACK;
			return true;
		}
		//寻找插入位置
		else
		{	
			while (cur)
			{
				//记录父节点的位置,便于后续的链接操作
				parent = cur;
				//向左遍历
				if (cur->_kv.first > kv.first)
				{
					cur = cur->_left;
				}
				//向右遍历
				else if (cur->_kv.first < kv.first)
				{
					cur = cur->_right;
				}
				//已有
				else return false;
			}
			cur = new Node(kv);
			//插入+链接
			if (parent->_kv.first > kv.first)
			{
				parent->_left = cur;
			}
			else
			{
				parent->_right = cur;
			}
			//链接
			cur->_parent = parent;
		}
		//父亲为红,变色调整
		while (parent && parent->_cl == RED)
		{
			//以下代码暂时省略......
			
			//1、parent为grandparent的左
			//情况一:uncle存在且为红,变色即可
			//无需讨论cur的位置,变色即可
			//情况二:uncle不存在或者为黑,旋转+变色
			//需讨论cur的位置
					//(1)cur为parent的左(进行右单旋)
					//	 g
					// p   u
					//c
					//(2)cur为parent的右(进行左右双旋)
					//	 g
					// p   u
					//	c
			
			//2、parent为grandparent的右
				//情况一:uncle存在且为红,变色即可
				//无需讨论cur的位置,变色即可
				//情况二:uncle不存在或者为黑,旋转+变色
					//(1)cur为parent的右(进行左单旋)
					//	 g
					// u   p
					//		c
					//(2)cur为parent的右(进行右左双旋)
					//	 g
					// u   p
					//	 c
		}	
		//保持根部为黑色
		_root->_cl = BLACK;
		return true;
	}

注意:所有的情况解决方案都是根据规律总结出来的,由于将所有的情况列出来过于复杂,所以用抽象的子树来代替(三角形代表的就是抽象的子树),在这一情况中将会告诉你如果不用抽象子树来代替会有多复杂

(1)情况一:parent为grandparent的左

1.1uncle存在且为红,变色即可

Node* uncle = grandParent->_right;
if (uncle && uncle->_cl == RED)
{
	parent->_cl = uncle->_cl = BLACK;
	grandParent->_cl = RED;
	//继续向上调整
	cur = grandParent;
	parent = cur->_parent;
}

在这里插入图片描述
三角形表示抽象子树,下面会举例说明这里为什么会用抽象子树来表示
在这里插入图片描述
当c,d,e是没有黑色节点的子树,此时cur是新增,此时c,d,e子树处为一个红节点或者为空,a,b为空。我们只需要通过新插入节点以及p,u、g节点进行变色即可,如图将p,u变黑,将g变红即可
在这里插入图片描述
当c,d,e子树是包含一个黑色节点的子树,如图此时c(cur)便不是新插入的节点了,最下边的红色节点才是
在这里插入图片描述

这是c,d,e子树可能的情况,然而插入位置有4个,cde形状组合:444,合计组合为64*4 = 256种情况,如果c,d,e子树是包含2个黑色节点及以上的,那么情况是分析不完的
在这里插入图片描述
其实你会发现,只要满足某个规律(如下)
在这里插入图片描述
然后做出应有的调整
在这里插入图片描述
最后在向上调整就好了
在这里插入图片描述
以下情况(非必须)都会用抽象子树来模拟规律

1.2uncle不存在或者为黑,旋转+变色

1.2.1cur为parent的左(进行右单旋)
//(1)cur为parent的左
//	 g
// p   u
//c
if (cur == parent->_left)
{
	RotateR(grandParent);
	grandParent->_cl = RED;
	parent->_cl = BLACK;
}

为了能让你更好的理解。此情况(uncle不存在或者为黑)的调整将会结合uncle存在且为红,变色即可的情况
如图:c、d、e是包含一个黑色节点的子树
在这里插入图片描述

规律:uncle存在且为红,变色即可并向上调整
在这里插入图片描述
我们将会发现此时满足u为黑
在这里插入图片描述

右旋
在这里插入图片描述
在这里插入图片描述
变色
在这里插入图片描述
注意c、d、e是包含一个黑色节点的子树
此时你便可以看看是否满足红黑树的性质了
在这里插入图片描述

1.2.2cur为parent的右(进行右左双旋)
//(1)cur为parent的右
//	 g
// p   u
//	c
else
{
	//左右双旋
	RotateL(parent);
	RotateR(grandParent);
	grandParent->_cl = RED;
	cur->_cl = BLACK;
}

在这里插入图片描述

(2)情况二:parent为grandparent的右

2.1uncle存在且为红,变色即可

Node* uncle = grandParent->_left;
if (uncle && uncle->_cl == RED)
{
	parent->_cl = uncle->_cl = BLACK;
	grandParent->_cl = RED;
	//继续向上调整
	cur = grandParent;
	parent = cur->_parent;
}

在这里插入图片描述

2.2uncle不存在或者为黑,旋转+变色

2.2.1cur为parent的右(进行左单旋)
//(1)cur为parent的右(左单旋)
//	 g
// u   p
//		c
if (cur == parent->_right)
{
	RotateL(grandParent);
	grandParent->_cl = RED;
	parent->_cl = BLACK;
}

在这里插入图片描述

2.2.2cur为parent的左(进行右左双旋)
//(1)cur为parent的右(右左双旋)
//	 g
// u   p
//	 c
RotateR(parent);
RotateL(grandParent);
grandParent->_cl = RED;
cur->_cl = BLACK;

在这里插入图片描述

5 全部代码

enum Color
{
	RED,
	BLACK
};
template<class K, class V>
struct RBTreeNode
{
	RBTreeNode* _left;
	RBTreeNode* _right;
	RBTreeNode* _parent;
	RBTreeNode* _pHead;
	Color _cl;
	pair<K, V> _kv;
template<class K, class V>
	RBTreeNode(const pair<K, V>& kv)
		: _left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		,_pHead(nullptr)
		, _cl(RED)
		, _kv(kv)
	{}
};

template<class K, class V>
class RBTree
{
	typedef RBTreeNode<K, V> Node;
public:
	//左单旋
	void RotateL(Node* pParent)
	{

		Node* subR = pParent->_right;
		Node* subRL = subR->_left;	//可能为空


		pParent->_right = subRL;
		subR->_left = pParent;

		Node* pPnode = pParent->_parent; //原来父亲的父亲

		pParent->_parent = subR;
		//可能为空
		if (subRL)
		{
			subRL->_parent = pParent;
		}
		
		//链接:旋转整棵树
		if (_root == pParent)
		{
			_root = subR;
			subR->_parent = nullptr;
		}
		//链接:旋转子树
		else
		{
			if (pPnode->_left == pParent)
			{
				pPnode->_left = subR;
			}
			else if (pPnode->_right == pParent)
			{
				pPnode->_right = subR;
			}
			subR->_parent = pPnode;
		}
	}
	// 右单旋
	void RotateR(Node* pParent)
	{

		Node* subL = pParent->_left;
		Node* subLR = subL->_right;

		pParent->_left = subLR;
		//可能为空
		if (subLR)
		{
			subLR->_parent = pParent;
		}

		Node* pPnode = pParent->_parent;

		subL->_right = pParent;
		pParent->_parent = subL;

		//旋转部分子树
		if (_root == pParent)
		{			
			_root = subL;
			subL->_parent = nullptr;
		}
		//旋转整棵子树
		else
		{
			if (pPnode->_left == pParent)
			{
				pPnode->_left = subL;
			}
			else
			{
				pPnode->_right = subL;
			}
			subL->_parent = pPnode;
		}
	}
	bool Insert(const pair<K, V>& kv)
	{				
		//记录当前节点
		Node* cur = _root;
		//记录父节点
		Node* parent = _root;
		//判断是否为空树
		if (_root == nullptr)
		{
			//直接插入
			_root = new Node(kv);
			//插入成功
			_root->_cl = BLACK;
			return true;
		}
		//寻找插入位置
		else
		{	
			while (cur)
			{
				//记录父节点的位置,便于后续的链接操作
				parent = cur;
				//向左遍历
				if (cur->_kv.first > kv.first)
				{
					cur = cur->_left;
				}
				//向右遍历
				else if (cur->_kv.first < kv.first)
				{
					cur = cur->_right;
				}
				//已有
				else return false;
			}
			cur = new Node(kv);
			//插入+链接
			if (parent->_kv.first > kv.first)
			{
				parent->_left = cur;
			}
			else
			{
				parent->_right = cur;
			}
			//链接
			cur->_parent = parent;
		}
		//父亲为红,变色调整
		while (parent && parent->_cl == RED)
		{
			//记录原来节点的父亲的父节点
			Node* grandParent = parent->_parent;
			//情况一:uncle存在且为红,变色即可
			if (parent == grandParent->_left)
			{
				Node* uncle = grandParent->_right;
				if (uncle && uncle->_cl == RED)
				{
					parent->_cl = uncle->_cl = BLACK;
					grandParent->_cl = RED;
					//继续向上调整
					cur = grandParent;
					parent = cur->_parent;
				}
				//情况二:uncle不存在或者为黑,旋转+变色
				else
				{
					//(1)cur为parent的左
					//	 g
					// p   u
					//c
					if (cur == parent->_left)
					{
						RotateR(grandParent);
						grandParent->_cl = RED;
						parent->_cl = BLACK;
					}
					//(1)cur为parent的右
					//	 g
					// p   u
					//	c
					else
					{
						//左右双旋
						RotateL(parent);
						RotateR(grandParent);
						grandParent->_cl = RED;
						cur->_cl = BLACK;
					}
					break;
				}
			
			}
			//parent为grandparent的右
			else
			{
				//情况一:uncle存在且为红,变色即可
				Node* uncle = grandParent->_left;
				if (uncle && uncle->_cl == RED)
				{
					parent->_cl = uncle->_cl = BLACK;
					grandParent->_cl = RED;
					//继续向上调整
					cur = grandParent;
					parent = cur->_parent;
				}
				//情况二:uncle不存在或者为黑,旋转+变色
				else
				{
					//(1)cur为parent的右(左单旋)
					//	 g
					// u   p
					//		c
					if (cur == parent->_right)
					{
						RotateL(grandParent);
						grandParent->_cl = RED;
						parent->_cl = BLACK;
					}
					//(1)cur为parent的右(左单旋)
					else
					{
						//(1)cur为parent的右(右左双旋)
						//	 g
						// u   p
						//	 c
						RotateR(parent);
						RotateL(grandParent);
						grandParent->_cl = RED;
						cur->_cl = BLACK;
					}
					break;
				}
				
			}
		}	
		//保持根部为黑色
		_root->_cl = BLACK;
		return true;
	}

在这里插入图片描述

小结

今日的分享就到这里啦,如果本文存在疏漏或错误的的地方,还请您能够指出

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

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

相关文章

HarmonyOs 4 (二) HelloWord

目录 一 开发工具下载安装1.1 下载安装包1.2 下载相关依赖 二 开发者注册与个人实名认证三 第一个程序2.1 创建第一个程序2.2 认识开发者界面2.3 目录结构认识2.3.1 父目录认识2.3.2 AppScope 目录2.3.3 entry目录2.3.3.1 ets 目录2.3.3.2 resources目录 2.3.4 认识配置文件2.3…

leetCode 90.子集 II + 回溯算法 + 图解 + 笔记

给你一个整数数组 nums &#xff0c;其中可能包含重复元素&#xff0c;请你返回该数组所有可能的子集&#xff08;幂集&#xff09;。 解集 不能 包含重复的子集。返回的解集中&#xff0c;子集可以按 任意顺序 排列 示例 1&#xff1a; 输入&#xff1a;nums [1,2,2] 输出…

k8s报错

报错&#xff1a; 这个错误信息表明你的容器运行时&#xff08;container runtime&#xff09;没有正常运行&#xff0c;具体是因为CRI&#xff08;容器运行时接口&#xff09;v1版本的API没有为特定的端点实现。这通常发生在使用containerd作为容器运行时时。错误信息中提到的…

Java集合(二)

1. Map 1.1 HashMap 和 Hashtable 的区别 线程是否安全&#xff1a; HashMap 是非线程安全的&#xff0c;Hashtable 是线程安全的,因为 Hashtable 内部的方法基本都经过synchronized 修饰。&#xff08;如果你要保证线程安全的话就使用 ConcurrentHashMap 吧&#xff01;&…

生物教师个人简历(精选21篇)

以下21篇简历内容以生物教师招聘需求为背景制作&#xff0c;大家可以灵活借鉴&#xff0c;希望能帮助大家在众多候选人中脱颖而出。 生物教师个人简历下载&#xff08;在线制作&#xff09;&#xff1a;百度幻主简历或huanzhuv.com 生物老师简历1&#xff1a; 求职意向 求职…

手机文件怎么传到电脑?简单方法分享!

将手机文件传输到电脑可以将其备份&#xff0c;以防数据丢失或意外情况发生。并且电脑具有更强大的处理能力&#xff0c;可以将文件进行编辑、修改、转换等操作&#xff0c;大大提高了工作效率。那么&#xff0c;手机文件怎么传到电脑&#xff1f;本文将为大家提供简单易懂的解…

nodejs微信小程序+python+PHP学科建设管理信息系统的设计与实现-计算机毕业设计推荐

目 录 摘 要 I ABSTRACT II 目 录 II 第1章 绪论 1 1.1背景及意义 1 1.2 国内外研究概况 1 1.3 研究的内容 1 第2章 相关技术 3 2.1 nodejs简介 4 2.2 express框架介绍 6 2.4 MySQL数据库 4 第3章 系统分析 5 3.1 需求分析 5 3.2 系统可行性分析 5 3.2.1技术可行性&#xff1a;…

【二分查找】LeetCode1970:你能穿过矩阵的最后一天

本文涉及的基础知识点 二分查找算法合集 作者推荐 动态规划LeetCode2552&#xff1a;优化了6版的1324模式 题目 给你一个下标从 1 开始的二进制矩阵&#xff0c;其中 0 表示陆地&#xff0c;1 表示水域。同时给你 row 和 col 分别表示矩阵中行和列的数目。 一开始在第 0 …

Python基础学习快速入门

文章目录 Number变量String字符串Def函数Class类List列表Tuple元组Dictionary字典Set集合值与引用类型if条件控制Loop循环 Number变量 python直接赋值&#xff0c;不需要定义变量类型。不需要**,逗号结尾符 使用print**直接进行输出 #赋值 a 1.0 print(a)a 7 print(a)p…

C语言错误处理之 “<errno.h>与<error.h>”

目录 前言 错误号处理方式 errno.h头文件 error.h头文件 参数解释&#xff1a; 关于的”__attribute__“解释&#xff1a; 关于“属性”的解释&#xff1a; 实例一&#xff1a; 实例二&#xff1a; error.h与errno.h的区别 补充内容&#xff1a; 前言 在开始学习…

[论文精读]利用大语言模型对扩散模型进行自我修正

本博客是一篇最新论文的精读&#xff0c;论文为UC伯克利大学相关研究者新近(2023.11.27)在arxiv上上传的《Self-correcting LLM-controlled Diffusion Models》 。 内容提要: 现有的基于扩散的文本到图像生成模型在生成与复杂提示精确对齐的图像时仍然存在困难,尤其是需要数值和…

目标检测——Fast R-CNN算法解读

论文&#xff1a;Fast R-CNN 作者&#xff1a;Ross Girshick 链接&#xff1a;https://arxiv.org/abs/1504.08083 代码&#xff1a;https://github.com/rbgirshick/fast-rcnn 目录 1、算法概述2、Fast R-CNN细节2.1The RoI pooling layer2.2 Fine-tuning for detection2.3 Fast…

【Android知识笔记】架构专题(三)

如何用工程手段,提高写代码的生产力?(元编程) 即如何写同样多的代码,花费更少的时间?如何自动生成代码,哪种代码可以被自动生成?哪些环节能够作为自动生成代码的切入点? 代码自动生成技术 代码自动生成,指的并不是让计算机凭自己的意愿生成代码。而是让预先实现好…

class-dump 混淆加固、保护与优化原理

​ 进行逆向时&#xff0c;经常需要dump可执行文件的头文件&#xff0c;用以确定类信息和方法信息&#xff0c;为hook相关方法提供更加详细的数据.class-dump的主要用于检查存储在Mach O文件的Objective-C中的运行时信息&#xff0c;为类&#xff0c;类别和协议生成声明信息&am…

熬夜会秃头——beta冲刺Day3

这个作业属于哪个课程2301-计算机学院-软件工程社区-CSDN社区云这个作业要求在哪里团队作业—beta冲刺事后诸葛亮-CSDN社区这个作业的目标记录beta冲刺Day3团队名称熬夜会秃头团队置顶集合随笔链接熬夜会秃头——Beta冲刺置顶随笔-CSDN社区 目录 一、团队成员会议总结 1、成员…

shareMouse 使用中遇到的问题

一、shareMouse 使用中遇到的问题 1、鼠标不能移动到另一个显示器 明明是两个显示器&#xff0c;但是 只显示一个&#xff0c;鼠标也不能移到另一个显示器上 后来&#xff0c; 设置了 wrap mouse pointer around display就好了&#xff0c;虽然还是显示一个显示器&#xff0c…

012 OpenCV sobel边缘检测

目录 一、环境 二、soble原理介绍 三、源码实验 一、环境 本文使用环境为&#xff1a; Windows10Python 3.9.17opencv-python 4.8.0.74 二、soble原理介绍 Sobel边缘检测是一种广泛应用于图像处理领域的边缘检测算法&#xff0c;它通过计算图像灰度函数在水平方向和垂直…

关于加强型葡萄酒的类型有哪些?

加强型葡萄酒指的是在酿造过程中或酿造完后&#xff0c;添加酒精提高葡萄酒酒精度的葡萄酒&#xff0c;一般加强型葡萄酒的酒精度数都能达到15度以上。那么&#xff0c;云仓酒庄的品牌雷盛红酒分享加强型葡萄酒的类型有哪些呢&#xff1f; 云仓酒庄多品牌多代言运营模式&#…

光伏开发过程中如何准确的进行测绘和设计?

光伏开发过程中&#xff0c;测绘和设计是至关重要的环节。准确的测绘和设计能够确保光伏项目的顺利实施&#xff0c;提高能源转换效率&#xff0c;降低 成本&#xff0c;同时也有利于环境保护。那么&#xff0c;如何准确地进行测绘和设计呢&#xff1f; 首先&#xff0c;要进行…

fastmock如何判断头信息headers中的属性值

fastmock可以快速提供后端接口的ajax服务。 那么&#xff0c;如何判断头信息headers中的属性值呢&#xff1f; 可以通过function中的参数_req可以获得headers中的属性值&#xff0c;比如 User-Agent&#xff0c;由于User-Agent属性带有特殊符号&#xff0c;因此使用[]方式而不…