map和set的使用和底层实现

news2024/12/28 20:26:43

在这里插入图片描述

嗨喽大家好,时隔许久阿鑫又给大家带来了新的博客,c++进阶——map和set的使用和底层实现,下面让我们开始今天的学习吧!

map和set的使用和底层实现

1.set和multiset的使用

set中的find和algorithm库中find的区别
在这里插入图片描述
在这里插入图片描述
删除一段区间的值,如删除[30,60]之间的值
在这里插入图片描述
在这里插入图片描述
区别:find时,多个x在树中,返回中序的第一个x

2.map和multimap的使用

在这里插入图片描述

2.1 键值对

用来表示具有一一对应关系的一种结构,该结构中一般只包含两个成员变量key和value,key代
表键值,value表示与key对应的信息
。比如:现在要建立一个英汉互译的字典,那该字典中必然
有英文单词与其对应的中文含义,而且,英文单词与其中文含义是一一对应的关系,即通过该应
该单词,在词典中就可以找到与其对应的中文含义。
在这里插入图片描述

template <class T1, class T2>
struct pair
{
typedef T1 first_type;
typedef T2 second_type;
T1 first;
T2 second;
pair(): first(T1()), second(T2())
{}
pair(const T1& a, const T2& b): first(a), second(b)
{}
};

2.2map中对象的插入

int main()
{
	//map<string, string> dict;
	//pair<string, string> kv1("left", "左边");
	//dict.insert(kv1);

	//dict.insert(pair<string, string>("right", "右边"));

	//dict.insert(make_pair("insert", "插入"));

	pair<string, string> kv2 = {"string","字符串" };
	//dict.insert({ "string", "字符串" });

	map<string, string> dict = { {"left", "左边"}, {"right", "右边"},{"insert", "插入"},{ "string", "字符串" } };

	map<string, string>::iterator it = dict.begin();
	while (it != dict.end())
	{
		//cout << (*it).first <<":"<<(*it).second << endl;
		//cout << (*it).first << ":" << (*it).second << endl;
		cout << it->first << ":" << it->second << endl;

		++it;
	}
	cout << endl;

	for (const auto& e : dict)
	{
		cout << e.first << ":" << e.second << endl;
	}
	cout << endl;
	return 0;
}
///
int main(){
string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉" };
	map<string, int> countTree;
	for (const auto& str : arr)
	{
		// 先查找水果在不在搜索树中
		// 1、不在,说明水果第一次出现,则插入<水果, 1>
		// 2、在,则查找到的节点中水果对应的次数++
		//BSTreeNode<string, int>* ret = countTree.Find(str);
		auto ret = countTree.find(str);
		if (ret == countTree.end())
		{
			countTree.insert({ str, 1 });
		}
		else
		{
			ret->second++;
		}
	}

	for (const auto& e : countTree)
	{
		cout << e.first << ":" << e.second << endl;
	}
	cout << endl;
	return 0;
}

在这里插入图片描述

it是对象,*it是返回结构体,((it->)->)中的(it->)是返回指向结构体的指针(&it)。

2.3map中的operator[]

分为插入成功和插入失败,调用insert检查key是否存在
插入成功:调用insert,构造出一个节点返回new_iterator和true,返回新迭代器的V&
插入失败:调用insert,返回与当前key值相同的迭代器和false,返回该迭代器的V&

在这里插入图片描述

int main()
{
	map<string, string> dict;
	dict.insert(make_pair("sort", "排序"));
	// 插入+修改
	dict["left"] = "左边";

	// 修改
	dict["left"] = "左边、剩余";

	// key不存在->插入 <"insert", "">
	dict["insert"];

	// key存在->查找
	cout << dict["left"] << endl;


	return 0;
}
int main()
{
string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉" };
	map<string, int> countTree;
	for (const auto& str : arr)
	{
		countTree[str]++;
	}

	for (const auto& e : countTree)
	{
		cout << e.first << ":" << e.second << endl;
	}
	cout << endl;
	return 0;
}

2.4map在oj中的应用

通过一个key去寻找一个value

  1. 随机链表的复制问题在于原节点指针和拷贝节点指针的映射关系
    在这里插入图片描述
class Solution {
public:
    map<Node*,Node*> p1;//通过map来查找原节点地址和拷贝节点地址的相对映射关系,key是原节点指针
    Node* copyRandomList(Node* head) {
        if(head==nullptr)
        return nullptr;

        Node* prev = new Node(head->val);//拷贝节点的头节点
        Node* headCopy = prev;//拷贝节点的头节点

        p1[head] = headCopy;
        Node* cur = head->next;
        while(cur)
        {
            Node* newnode = new Node(cur->val);
            p1[cur] = newnode;
            prev->next = newnode;
            prev = prev->next;
            cur = cur->next;
        }
        Node* head1 = headCopy;//拷贝节点的头节点
        while(head)
        {
            //当前节点的random在拷贝节点中的位置
            headCopy->random = p1[head->random];
            headCopy = headCopy->next;
            head = head->next;
        }
        return head1;
    }
};

3.底层结构

3.1 AVL 树

3.1.1 AVL树的概念

二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查
找元素相当于在顺序表中搜索元素,效率低下。因此,两位俄罗斯的数学家G.M.Adelson-Velskii
和E.M.Landis在1962年
发明了一种解决上述问题的方法:当向二叉搜索树中插入新结点后,如果能保证每个结点的左右
子树高度之差的绝对值不超过1(需要对树中的结点进行调整),即可降低树的高度,从而减少平均
搜索长度。
一棵AVL树或者是空树,或者是具有以下性质的二叉搜索树:

  1. 它的左右子树都是AVL树
  2. 左右子树高度之差(简称平衡因子(右子树的高度减去左子树的高度))的绝对值不超过1(-1/0/1)
    如果一棵二叉搜索树是高度平衡的,它就是AVL树。如果它有n个结点,其高度可保持在
    O ( l o g 2 n ) O(log_2 n) O(log2n),搜索时间复杂度O( l o g 2 n log_2 n log2n)
    在这里插入图片描述
    在这里插入图片描述
3.1.2 AVL树的旋转

如果在一棵原本是平衡的AVL树中插入一个新节点,可能造成不平衡,此时必须调整树的结构,
使之平衡化。根据节点插入位置的不同,AVL树的旋转分为四种:
下列图为抽象图,可能是一整棵树,也可能是子树
h是>=0的AVL树

  1. 新节点插入较高左子树的左侧—左左:右单旋
    在这里插入图片描述

  2. 新节点插入较高右子树的右侧—右右:左单旋
    在这里插入图片描述

  3. 新节点插入较高左子树的右侧—左右:先左单旋再右单旋
    只进行左旋时
    在这里插入图片描述
    在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

  1. 新节点插入较高右子树的左侧—右左:先右单旋再左单旋

若旋转节点的右子树高于左子树,则以右节点为根的子树也必须是右子树高于左子树(因为单纯的左旋或右旋已经无法保持旋转后的二叉搜索树依旧是AVL树了)
否则就需要先以右节点为旋转节点进行右旋,再以根节点为旋转节点进行左旋
右左双旋需要标记三个节点的原因是,先进行旋转的是子树,只要旋转就需要标记两个节点
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.2 红黑树

3.2.1 红黑树的概念

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

3.2.2 红黑树的性质

在这里插入图片描述

在这里插入图片描述

3.2.3 红黑树的插入

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

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

在这里插入图片描述
2.abcde不为空,cur开始是黑色,以cur为根的红黑树满足情况一,cur后来变为红色。
在这里插入图片描述

在这里插入图片描述
1.u不存在,abcde都是空,cur是新增节点
需要注意的是,对于双旋(情况三),先进行单旋就变成情况二
在这里插入图片描述

2.abcde不为空,cur开始是黑,cur为根的红黑树满足情况一,cur由情况一变为红色
在这里插入图片描述
双旋(情况三)先进行单旋变为情况二
在这里插入图片描述

while (parent && parent->_col == RED)
		{
			Node* grandfather = parent->_parent;
			//    g
			//  p   u
			if (parent == grandfather->_left)
			{
				Node* uncle = grandfather->_right;
				if (uncle && uncle->_col == RED)
				{
					// u存在且为红 -》变色再继续往上处理
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;

					cur = grandfather;
					parent = cur->_parent;
				}
				else
				{
					// u存在且为黑或不存在 -》旋转+变色
					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
			{
				//    g
				//  u   p
				Node* uncle = grandfather->_left;
				// 叔叔存在且为红,-》变色即可
				if (uncle && uncle->_col == RED)
				{
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;

					// 继续往上处理
					cur = grandfather;
					parent = cur->_parent;
				}
				else // 叔叔不存在,或者存在且为黑
				{
					// 情况二:叔叔不存在或者存在且为黑
					// 旋转+变色
					//      g
					//   u     p
					//            c
					if (cur == parent->_right)
					{
						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;
	}
3.2.4计算每条路径上黑色节点的个数

从空节点开始往回推理,每一个节点都可以当作一个子树的根,将一颗二叉树看成有限个子树构成
完成二叉树相关的题目,需要将一颗二叉树的大问题拆分为有限个子树的小问题

    //先选取一个参考值,别的路径上的黑色节点的个数都和参考值进行比较
	bool IsBalance()
	{
		if (_root == nullptr)
			return true;

		if (_root->_col == RED)
		{
			return false;
		}
		
		// 参考值
		int refNum = 0;
		Node* cur = _root;
		while (cur)
		{
			if (cur->_col == BLACK)
			{
				++refNum;
			}

			cur = cur->_left;
		}

		return Check(_root, 0, refNum);
	}
	
      bool Check(Node* root, int blackNum, const int refNum)
	     {
		if (root == nullptr)
		    {
			//cout << blackNum << endl;
			if (refNum != blackNum)
			{
				cout << "存在黑色节点的数量不相等的路径" << endl;
				return false;
			}

			return true;
		}

		if (root->_col == RED && root->_parent->_col == RED)
		{
			cout << root->_kv.first << "存在连续的红色节点" << endl;
			return false;
		}

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

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

3.3通过红黑树实现map和set

利用同一颗红黑树实现map和set(是一种红黑树泛型的实现)
在这里插入图片描述
对于map和set来说,第一个模板参数存在的意义是用来查找Key
在这里插入图片描述

为了同时实现map和set内部数据的比较大小,需要实现一个另类的仿函数

只要记住模板接受不同的类型参数就能实例化出不同的对象

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.3.1 红黑树的迭代器

分为当前节点右子树为空和不为空两种情况
有右子树说明该子树还没遍历完,找右子树最左节点(以右节点为根的最小节点)
无右子树说明该子树已经遍历到根,需要跳转到孩子是父亲左的那个祖先节点
反向迭代器和正向迭代器相反,分为左子树为空和左子树不为空两种情况
在这里插入图片描述
RBTree.h中的成员函数用大写,Map和Set中的成员函数用小写来区分。

template<class T, class Ref, class Ptr>
struct RBTreeIterator
{
	typedef RBTreeNode<T> Node;
	typedef RBTreeIterator<T, Ref, Ptr> Self;

	Node* _node;
	Node* _root;

	RBTreeIterator(Node* node, Node* root)
		:_node(node)
		,_root(root)
	{}

	Self& operator++()
	{
		if (_node->_right)
		{
			// 右不为空,右子树最左节点就是中序第一个
			Node* leftMost = _node->_right;
			while (leftMost->_left)
			{
				leftMost = leftMost->_left;
			}

			_node = leftMost;
		}
		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 == nullptr) // end()
		{
			// --end(),特殊处理,走到中序最后一个节点,整棵树的最右节点
			Node* rightMost = _root;
			while (rightMost && rightMost->_right)
			{
				rightMost = rightMost->_right;
			}

			_node = rightMost;
		}
		else if (_node->_left)
		{
			// 左子树不为空,中序左子树最后一个
			Node* rightMost = _node->_left;
			while (rightMost->_right)
			{
				rightMost = rightMost->_right;
			}

			_node = rightMost;
		}
		else
		{
			// 孩子是父亲右的那个祖先
			Node* cur = _node;
			Node* parent = cur->_parent;
			while (parent && cur == parent->_left)
			{
				cur = parent;
				parent = cur->_parent;
			}

			_node = parent;

		}

		return *this;
	}

	Ref operator*()
	{
		return _node->_data;
	}

	Ptr operator->()
	{
		return &_node->_data;
	}

	bool operator!= (const Self& s)
	{
		return _node != s._node;
	}

	bool operator== (const Self& s)
	{
		return _node == s._node;
	}
};

在MyMap.h中

namespace bit
{
	template<class K, class V>
	class map
	{
		struct MapKeyOfT
		{
			const K& operator()(const pair<K, V>& kv)
			{
				return kv.first;
			}
		};
	public:
		typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::Iterator iterator;//typename告知编译器iterator是一个类型名
		typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::ConstIterator const_iterator;

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

		iterator end()
		{
			return _t.End();
		}

		const_iterator begin() const
		{
			return _t.Begin();
		}

		const_iterator end() const
		{
			return _t.End();
		}

		pair<iterator, bool> insert(const pair<K, V>& kv)
		{
			return _t.Insert(kv);
		}

		iterator find(const K& key)
		{
			return _t.Find(key);
		}

		V& operator[](const K& key)
		{
			pair<iterator, bool> ret = insert(make_pair(key, V()));
			return ret.first->second;
		}

	private:
		RBTree<K, pair<const K, V>, MapKeyOfT> _t;
	};
}
3.3.2map和set不支持修改key

在map和set中不允许修改key,因为修改了key会破坏红黑树的特性
在这里插入图片描述

3.3.3map中的operator[]

在Insert中,由于Node* cur = new Node(data);
但是cur指针当红黑树需要旋转时会指向另外的节点,所以需要一个newnode来记录下新节点的地址。

pair<iterator, bool> insert(const pair<K, V>& kv)
{
	return _t.Insert(kv);
}

V& operator[](const K& key)
{
	pair<iterator, bool> ret = insert(make_pair(key, V()));
	return ret.first->second;
}

c++stl中的set中的compare = less<>

在这里插入图片描述

std::set<int, std::greater<int>> mySet;
#include <iostream>  
#include <set>  
#include <functional> // 对于 std::greater,但实际上包含 <set> 就足够了  
  
int main() {  
    std::set<int, std::greater<int>> mySet;  
  
    // 向集合中添加元素  
    mySet.insert(5);  
    mySet.insert(1);  
    mySet.insert(10);  
    mySet.insert(3);  
  
    // 遍历集合并打印元素  
    for (int elem : mySet) {  
        std::cout << elem << " ";  
    }  
    std::cout << std::endl;  
  
    return 0;  
}

在这里插入图片描述

std::set<int, std::greater> 并没有改变二叉树(特别是红黑树)的插入规则本身。红黑树的插入规则是固定的,包括如何旋转节点以保持树的平衡等。然而,它确实改变了元素之间的比较方式,这影响了插入过程中新元素在树中的位置
当你使用 std::greater 作为 std::set 的比较函数时,你实际上是在告诉集合:“我想要按照从大到小的顺序来存储元素。” 因此,当插入新元素时,集合会使用 std::greater 来比较新元素和树中已存在的元素,以确定新元素应该放在哪里。
这种比较方式的改变并没有修改红黑树的插入算法,而是改变了插入算法中用于比较元素的部分。因此,可以说它改变了元素在集合中的排序方式,而不是改变了二叉树的插入规则。插入规则仍然是红黑树的插入规则,只是比较元素的方式不同。

好啦,今天的内容我们就学习到这里,如果大家觉得阿鑫写的不错的话,记得留下你的一键三连哦,期待我们的下一次相遇!## 好啦,今天的内容我们就学习到这里,如果大家觉得阿鑫写的不错的话,记得留下你的一键三连哦,期待我们的下一次相遇!

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

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

相关文章

基于FCM模糊聚类算法的图像分割matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 4.1 FCM算法原理 4.2 图像分割中的应用 5.算法完整程序工程 1.算法运行效果图预览 (完整程序运行后无水印) 2.算法运行软件版本 matlab2022a 3.部分核心程序 &#xff08;完整版代码包…

单列表集合顶层接口Collection

List&#xff1a;添加元素是有序&#xff0c;可重复&#xff0c;有索引 Set&#xff1a;添加元素是无序&#xff0c;不重复&#xff0c;无索引 Collection是单列集合的祖宗接口&#xff0c;它的功能是全部单列集合都可以继承使用。 1.添加元素 细节1:如果我们要往List系列集…

ArcGIS出图格网小数位数设置

1、比如要去掉格网后面的小数点&#xff0c;如何设置呢&#xff1f; 2、如下图设置。

《软件工程导论》(第6版)第12章 面向对象实现 复习笔记

第12章 面向对象实现 一、面向对象实现概述 1&#xff0e;主要任务 &#xff08;1&#xff09;把面向对象设计结果翻译成用某种程序语言书写的面向对象程序。 &#xff08;2&#xff09;测试并调试面向对象的程序。 2&#xff0e;面向对象程序质量的影响因素 &#xff0…

Redis Pub/Sub模式:分布式系统中的解耦利器

序言 Redis的发布订阅&#xff08;Pub/Sub&#xff09;是一种消息通信模式&#xff0c;允许发布者&#xff08;Publisher&#xff09;发送消息到频道&#xff08;Channel&#xff09;&#xff0c;而订阅者&#xff08;Subscriber&#xff09;可以订阅一个或多个频道来接收消息…

惠中科技光伏清洗剂:绿色清洁,高效发电的守护者

在当今全球能源转型的大背景下&#xff0c;光伏产业作为绿色能源的重要组成部分&#xff0c;正以前所未有的速度蓬勃发展。然而&#xff0c;光伏板长期暴露于户外环境&#xff0c;不可避免地会遭受灰尘、鸟粪、油污等污染物的侵袭&#xff0c;这些污染物如同阴影般覆盖在光伏板…

代码随想录Day 35|动态规划,二维dp数组,滚动数组,leetcode题目:416.分割等和子集

提示&#xff1a;DDU&#xff0c;供自己复习使用。欢迎大家前来讨论~ 文章目录 动态规划Part03一、 动态规划&#xff1a;01背包理论基础01 背包二维dp数组01背包 二、动态规划&#xff1a;01背包理论基础&#xff08;滚动数组&#xff09;思路一维dp数组&#xff08;滚动数组&…

echarts三维立体扇形图+三维立体环形图配置详解记录

先看效果&#xff0c;注&#xff1a;三维立体echarts比较吃性能&#xff0c;同一页面如果有多个三维图进行渲染&#xff0c;进行跳转时可呢能会对整体页面产生影响&#xff0c;具体解决方法可查看本人另一篇文章 多个echarts使用3D导致页面卡顿的解决办法 三维立体扇形图 三维…

c# Avalonia 架构开发跨平台应用

实现了一个计算器的应用&#xff0c;先看在不同平台的效果 windows11上 ubuntu上 统信UOS 上 麒麟 kylin v10 好了&#xff0c;先说一下问题&#xff0c;如果想一套代码在不同平台同时运行&#xff0c;里面调用的逻辑还是要分系统的&#xff0c;先分linux系统和windows系统&a…

2024年全国铁路(铁路、高铁、地铁)矢量数据集

数据更新时间​&#xff1a;2024年6月​&#xff1b; ​数据范围&#xff1a;全国各省&#xff08;不包含台湾&#xff09;; 数据格式​&#xff1a;shp; ​数据包含类型&#xff1a;铁路、高铁、地铁 数据​坐标信息&#xff1a; EPSG Code 4326 大地基准面 D_WGS_1…

CTFSHOWRCE

web3 1.打开环境&#xff0c;上面给了一句php的话&#xff0c;意思是get传参url有文件包含漏洞 2.get传参运用伪协议&#xff0c;post传参命令执行看目录。 3.上面有一个文件ctf_go_go_go,访问这个文件就有flag web4 1.打开环境&#xff0c;和上一关的一样&#xff0c;但是不…

CSS实现优惠券透明圆形镂空打孔效果等能力学习

前言&#xff1a;无他&#xff0c;仅供学习记录&#xff0c;通过一个简单的优惠券Demo实践巩固CSS知识。 本次案例主要学习或巩固一下几点&#xff1a; 实现一个简单的Modal&#xff1b;如何进行复制文本到粘贴板&#xff1b;在不使用UI的svg图片的情况下&#xff0c;如何用C…

【C++】模板特化

目录 一、非类型模板参数 二、模板的特化 &#x1f31f;概念 扩展小知识补充(1)&#xff1a; 扩展小知识补充(2)&#xff1a; &#x1f31f;函数模板特化 扩展小知识&#xff1a; &#x1f31f;类模板特化 ✨全特化 ✨偏特化 • 部分特化&#xff1a;将模板参数表中…

前端几种常见框架【第一节】

​ 大家好&#xff0c;我是程序员小羊&#xff01; 前言&#xff1a; 最近比较忙&#xff0c;本人在复习软考中级设计考试&#xff0c;所以本系列文从零基础开始复习软考到结束软考&#xff08;计算机技术与软件专业技术资格考试&#xff09;作为国家级职业资格认证考试&#x…

ROS2 2D相机基于AprilTag实现3D空间定位最简流程

文章目录 前言驱动安装下载安装方式一&#xff1a;方式二&#xff1a; 相机检测配置config文件编译、运行程序注意 内参标定标定板运行程序 apriltag空间定位标签打印下载安装可视化结果 前言 AprilTag是一种高性能的视觉标记系统&#xff0c;广泛应用于机器人导航、增强现实和…

简述CCS平面线性光源

光源在机器视觉系统中起着重要作用&#xff0c;不同环境、场景及应用合适光源都不一样&#xff0c;今天我们来看看LFX3-PT系列平面线性光源。它是最适合检测镜面物体的凹凸,外壳小巧的光源。备有根据检测条件可选的2种线间距。1mm型&#xff08;型号末尾&#xff1a;A&#xff…

【ArcGIS Pro第一期】界面简介

ArcGIS Pro简介 ArcGIS Pro界面简介1.1 打开工程1.2 使用功能区上的工具 参考 ArcGIS Pro 是一种基于功能区的应用程序。 ArcGIS Pro 窗口顶部的功能区有许多命令可供选择&#xff0c;而根据需要打开的各个窗格&#xff08;可停靠窗口&#xff09;中则提供了更为高级或专用的功…

erlang学习:用ETS和DETS存储数据

作用 ets和dets是两个系统模块&#xff0c;可以用来高效存储海量的Erlang数据。 ETS和DETS执行的任务基本相同&#xff1a;它们提供大型的键值查询表。ETS常驻内存&#xff0c;DETS则常驻磁盘。ETS是相当高效的&#xff1a;可以用它存储海量的数据&#xff08;只要有足够的内…

ACM模式 输入输出练习

牛客-练习地址 第一题 let cnt readline(); while(cnt--){let input readline()let arr input.split( ).map(Number)console.log(arr[0]arr[1]) }第二题 let cnt readline(); while(cnt--){let input readline()let arr input.split( ).map(Number)console.log(arr[0]ar…

Web攻防之应急响应(二)

目录 前提 &#x1f354;学习Java内存马前置知识 内存马 内存马的介绍 内存马的类型众多 内存马的存在形式 Java web的基础知识&#xff1a; Java内存马的排查思路&#xff1a; &#x1f354;开始查杀之前的需要准备 1.登录主机启动服务器 2.生成jsp马并连接成功 …