【C++进阶】2024年了set、map还搞不懂底层细节?

news2024/9/27 9:24:55
头像
🚀个人主页:@小羊
🚀所属专栏:C++
很荣幸您能阅读我的文章,诚请评论指点,欢迎欢迎 ~

动图描述

目录

  • 一、前情提要
    • 1、什么是关联式容器?
    • 2、键值对又是什么?
  • 二、树形结构的关联式容器
    • 1、set
      • 1.1 set 介绍
      • 1.2 set 模版参数列表
      • 1.3 set 的相关操作
    • 2、multiset
      • 2.1 multiset 介绍
      • 2.2 count 接口
      • 2.3 find 接口
      • 2.4 erase 接口
    • 3、map
      • 3.1 map介绍
      • 3.2 map的元素存储
      • 3.3 map的几种初始化方式
      • 3.4 使用map计数
      • 3.5 [ ]运算符重载
    • 4、multimap
      • 4.1 multimap 介绍


一、前情提要

在学习setmap之前,我们首先来了解一下 关联式容器键值对 这两个概念。

1、什么是关联式容器?

前面我们学习过的vectorListdequeforward_list等都是序列式容器,底层为线性序列的数据结构,里面存储的是元素本身。
关联式容器也是用来存储数据的,与序列式容器不同的是,关联式容器里面存的是<key, value>结构的键值对,在数据检索时比序列式容器效率更高。

基于红黑树的关联容器:

  • set:存储唯一键的集合
  • multiset:存储可以有重复键的集合
  • map:存储唯一键及其关联值的映射
  • multimap:存储可以有重复键及其关联值的映射
  • 特点:内部以红黑树实现,元素默认按键的升序排列,支持快速查找、插入和删除操作

2、键值对又是什么?

键值对是一种将两个相关的值组合在一起的数据结构,具有一一对应的关系,该结构一般只包含两个成员变量key和Value,key代表键值,Value代表与key对应的信息。

特点:

  • 唯一性:在大多数情况下,键(Key)是唯一的,这意味着一个键只能映射到一个值(Value)。然而,在某些特殊情况下(如multimap或unordered_multimap),一个键可以映射到多个值
  • 无序性:键值对的存储顺序通常不是由键或值的顺序决定的,除非使用了某种特定的排序算法或数据结构(如基于红黑树的map或set)。然而,有些容器(如unordered_map或unordered_set)确实提供了基于哈希表的快速查找,但它们的元素是无序的
  • 灵活性:键值对可以存储几乎任何类型的数据,只要键和值的数据类型在容器声明时指定或兼容

SGI-STL中键值对的定义:

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)
{}
};

通过上面简单的介绍了解一下关联式容器和键值对,我们再学习set和map会相对清晰一些。


二、树形结构的关联式容器

根据应用场景的不同,STL总共实现了两种不同结构的管理式容器:树形结构和哈希结构。本篇文章我们学习树形结构。

树形结构关联式容器主要包括map、set、multimap和multiset。这些容器通过键值对(key-value)的形式存储数据,并且基于树型结构(平衡搜索树,即红黑树)来实现,确保了容器中的元素是有序的。

1、set

1.1 set 介绍

  • set 文档介绍。
  • 使用set需要包头文件<set>

| set的底层使用二叉搜索树(红黑树)实现的,所以set具有以下的性质:

  • 元素唯一: set中的每个元素都是唯一的,不允许有重复的元素。如果尝试插入一个已存在的元素,该插入操作将被忽略,即set的大小不会增加
  • 快速查找: 由于 set 内部使用平衡二叉树(如红黑树)实现,因此它查找的速度很快,时间复杂度为O(LogN)
  • 元素不可修改: set中的元素不能在容器中修改(元素总是const)(因为这可能会破坏容器的排序顺序),但是可以从容器中插入或删除
  • 有序: 使用set的迭代器中序遍历set中的元素,可以得到有序序列(结合元素唯一和有序,set可以做到去重+排序

| 另外还有一些细节值得注意:

  • set中插入元素时,只需要插入value即可,不需要构造键值对
  • 与map/multimap不同,map/multimap中存储的是真正的键值对<key, value>,set中只放value,但在底层实际存放的是由<value, value>构成的键值对

1.2 set 模版参数列表

在这里插入图片描述

  • T: set中存放元素的类型,实际在底层存的是<value, value>的键值对
  • Compare: set中元素默认按小于比较,如果想要按大于比较可以传相应的仿函数
  • Alloc: set中元素空间的管理方式,使用STL提供的空间配置器管理

1.3 set 的相关操作

set的构造、迭代器、容量等函数比较简单,使用起来不是很复杂,童鞋们参考上面提供的文档介绍自己看一下哈,这里就不浪费篇幅了哈,我们介绍一下set的插入、删除、查找等操作。

头像
函数声明功能
pair<iterator,bool> insert (const value_type& val)在set中插入val,实际插入的是<val, val>构成的键值对,如果插入成功,返回<该元素在set中的位置,true>,如果插入失败(val在set中已经存在),返回<val在set中的位置,false>
void erase (iterator position)删除set中position位置的元素
size_type erase ( const key_type& x )删除键值为x的元素
void erase (iterator first, iterator last)删除set中[first, last)区间的元素
iterator find ( const key_type& x ) const返回set中值为x元素的位置,返回值不可被修改
void test1()
{
	构造空的set
	//set<int> s;
	//s.insert(1);
	//s.insert(3);
	//s.insert(1);
	//s.insert(6);
	//s.insert(2);
	//s.insert(3);
	//s.insert(5);
	//s.insert(8);

	int arr[] = { 3,4,9,6,5,7,1,5,6,4,6,4,2,4,6,8,5,4,1,5,6,6,3,2 };
	//迭代器区间构造
	set<int> s(arr, arr + sizeof(arr) / sizeof(int));

	//迭代器遍历
	//去重+排序
	auto it = s.begin();
	while (it != s.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;

	//删除最小的元素
	s.erase(s.begin());

	//范围for遍历
	for (auto e : s)
	{
		cout << e << " ";
	}
	cout << endl;
}

2、multiset

2.1 multiset 介绍

  • multiset 文档介绍
  • 使用multiset也需要包含头文件<set>

multisetset的操作、使用大致相同,所以这里也就不再赘述了(又能偷懒了),只是在某些方面有一些区别:

  • multiset也是按照特定顺序存储元素的容器,不过其元素是可以重复的
  • multiset的插入接口只需要插入即可,因为理论上multiset的插入操作一定会成功
  • multiset不会对数据去重,所以可以真正意义上的排序

另外multisetset也存在接口上的一些区别。


2.2 count 接口

  • count的作用是: 给一个值,返回这个值在当前容器中的个数。

set中也有count接口,不过set中的count接口用处不是很明显,可以用来判断某一个值在当前set中存不存在:

v

相比而言multiset中的count才能发挥它真正的作用:

在这里插入图片描述


2.3 find 接口

set中的find接口类似,multiset中的find接口作用也是在容器中搜索等效于val 的元素,如果找到,则返回指向val的迭代器,否则返回multiset::end

不过multiset中可能存在多个给定的val,而这时的find返回的是中序遍历的第一个val的迭代器,这么做的目的是能把所有的val都找出来。

找出multiset中所有的val:

int main()
{
	int arr[] = { 3,4,9,6,5,7,1,5,6,4,6,4,2,4,6,8,5,4,1,5,6,6,3,2 };
	multiset<int> mts(arr, arr + sizeof(arr) / sizeof(int));
	int x = 0;
	cin >> x;
	int y = mts.count(x);
	while (y--)
	{
		cout << *(mts.find(x)) << " ";
	}
	return 0;
}

在这里插入图片描述


2.4 erase 接口

支持通过迭代器、元素值、迭代器区间来删除元素。

函数声明参数
void erase (iterator position)迭代器
size_type erase (const value_type& val)val
void erase (iterator first, iterator last)迭代器区间

multiset的erase接口和set的erase虽然看起来好像一样,但实际上有不小的差别。

来看下面三种删除操作:

void test2()
{
	//传迭代器删除
	cout << "传迭代器删除" << endl;
	int arr1[] = { 3,4,9,6,5,7,1,5,6,4,6,4,2,4,6,8,5,4,1,5,6,6,3,2 };
	multiset<int> mts1(arr1, arr1 + sizeof(arr1) / sizeof(int));
	for (auto e : mts1)
	{
		cout << e << " ";
	}
	cout << endl;

	mts1.erase(mts1.find(6));
	multiset<int>::iterator it = mts1.begin();
	while (it != mts1.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;

	//传值删除
	cout << "传值删除" << endl;
	int arr2[] = { 3,4,9,6,5,7,1,5,6,4,6,4,2,4,6,8,5,4,1,5,6,6,3,2 };
	multiset<int> mts2(arr2, arr2 + sizeof(arr2) / sizeof(int));
	for (auto e : mts2)
	{
		cout << e << " ";
	}
	cout << endl;

	mts2.erase(6);
	for (auto e : mts2)
	{
		cout << e << " ";
	}
	cout << endl;

	//传迭代器区间删除
	cout << "传迭代器区间删除" << endl;
	int arr3[] = { 3,4,9,6,5,7,1,5,6,4,6,4,2,4,6,8,5,4,1,5,6,6,3,2 };
	multiset<int> mts3(arr3, arr3 + sizeof(arr3) / sizeof(int));
	for (auto e : mts3)
	{
		cout << e << " ";
	}
	cout << endl;

	mts3.erase(mts3.equal_range(6).first, mts3.equal_range(6).second);
	for (auto e : mts3)
	{
		cout << e << " ";
	}
	cout << endl;
}

在这里插入图片描述

通过结果我们不难得出:如果multiset中存在多个val,则传迭代器只会删除一个,传值和传迭代器区间会将multiset中所有的val删除。

这点是我们在平时使用时容易忽略的。


3、map

3.1 map介绍

  • map 文档介绍
  • 使用map需要包头文件<map>

| map的底层也是二叉平衡树(红黑树)实现的,所以其也具有以下性质:

  • 有序: map是关联容器,它按照特定的次序存储由键值key和值value组合而成的元素,map中的元素只按照键值key进行比较排序
  • 唯一: 每个键在map中都是唯一的(值可以不唯一),不允许有重复键。如果尝试插入一个已存在的键,则不会插入该元素,并返回此现有元素的迭代器
  • 时间复杂度: map的查找、插入和删除操作的时间复杂度通常是O(logN)

其实map和set还是非常相似的,甚至函数接口等,这里就不再过多列举了,可以对比set学习。
下面我们着重介绍map的键值对存储和[]重载。


3.2 map的元素存储

在map中,键值key通常用于排序和唯一地标识元素,而值value中存储与此键值key关联的内容。键值key和值value的类型可能不同,并且在map的内部,key与value通过成员类型value_type绑定在一起,为其取别名称为pair:

  • typedef pair<const key, T> value_type;

文章开头我们简单地了解了下键值对,可以看出pair 是一个模板结构体,它包含两个值,分别称为 first 和 second,分别对应于键(Key)和值(Value)在 map 中的位置。

当你向 map 插入一个元素时,你实际上是在插入一个 std::pair<Key, Value> 对象。同样地,当你从 map 中检索元素时,你得到的是一个 std::pair<const Key, Value> 的引用(或迭代器指向的对象),其中 Key 是 const 的,因为 map 的键在插入后不应该被修改。


3.3 map的几种初始化方式

  • 创建有名对象
  • 使用匿名对象
  • 使用make_pair构建一个匿名对象
  • 隐式类型转换
int main()
{
	map<string, string> m;

	//创建有名对象
	pair<string, string> p("front", "前");
	m.insert(p);

	//使用匿名对象
	m.insert(pair<string, string>("behind", "后"));
	
	//使用make_pair构建一个匿名对象
	m.insert(make_pair("left", "左"));
	
	//隐式类型转换
	m.insert({ "right", "右" });

	for (const auto& e : m)
	{
		cout << e.first << "->" << e.second << endl;
	}
	cout << endl;

	return 0;
}

在这里插入图片描述

从运行结果可以看出,我们插入的键值对是按照 “键”(英文单词)排序过的。

| 对其中make_pair隐式类型转换做如下解释:

  • make_pair是 C++ 标准库中的一个非常有用的函数模板,它定义在头文件<utility> 中,make_pair 的主要作用是创建一个 std::pair类型的对象
template <class T1,class T2>
pair<T1,T2> make_pair (T1 x, T2 y)
{
    return ( pair<T1,T2>(x,y) );
}
  • 使用make_pair的好处是可以自动推导类型,不需要我们指定了

  • 单参数的构造函数支持隐式类型转换,多参数也支持,只需要用{}括起来就可

除了以上的四种初始化方法,还有一种更为方便的方法。

| initializer_list:

C++11支持了使用initializer_list初始化列表来直接初始化map对象:

在这里插入图片描述
其中value_typepair<const key_type, mapped_type>的别名。

initializer_list是C++11标准中引入的一种新类型,它提供了一种统一且方便的方式来初始化对象,特别是用于构造函数和函数参数中,以允许传递一个初始化元素列表。

  • 它表示一个特定类型的值的数组,是一种轻量级的包装器,用于在编译时捕获花括号初始化的列表]
  • 提供了一种灵活且统一的方式来初始化对象和处理多个同类型值的列表
int main()
{
	map<string, string> m = { {"front", "前"},{"behind", "后"}, 
	{"left", "左"},{"right", "右"} };

	//范围for遍历
	for (const auto& e : m)
	{
		cout << e.first << "->" << e.second << endl;
	}
	cout << endl;

	//迭代器遍历
	map<string, string>::iterator it = m.begin();
	while (it != m.end())
	{
		cout << it->first << "->" << it->second << endl;
		++it;
	}

	return 0;
}

在这里插入图片描述

花括号外层是initializer_list,内层是隐式类型转换。


3.4 使用map计数

  • 假设我们需要统计字符出现的次数。

计数的规则是:key存储对应的字符,value为字符出现的次数。在map中找给定的字符,如果没找到就插入这个字符和出现的次数1;如果在map中找到了这个字符,我们就++对应的value。

这里简单介绍一下map中的find函数接口:find函数用于查找具有指定键的元素。如果找到了该元素,find函数会返回一个指向该元素的迭代器;如果没有找到,返回迭代器map::end()

void test1()
{
	char arr[] = { 'a','d','h','a','b','a','h','a','b','d','a',
	'h','d','b','d' };
	map<char, int> m;
	for (auto ch : arr)
	{
		map<char, int>::iterator it = m.find(ch);
		//没找到就插入
		if (it == m.end())
		{
			m.insert({ ch, 1 });
		}
		//找到就++对应的value
		else
		{
			++it->second;
		}
	}
	for (auto e : m)
	{
		cout << e.first << "->" << e.second << endl;
	}
}

在这里插入图片描述

除了上面的计数方法,还有一种更为简单的方法也可以实现计数:

void test2()
{
	char arr[] = { 'a','d','h','a','b','a','h','a','b','d','a',
	'h','d','b','d' };
	map<char, int> m;
	for (auto ch : arr)
	{
		m[ch]++;
	}
	for (auto e : m)
	{
		cout << e.first << "->" << e.second << endl;
	}
}

在这里插入图片描述

上面的计数实现依赖于map中重载了运算符[]。我们看到上面的实现过程似乎很简洁,那这个运算符重载究竟是怎么只用了一行简短的代码就实现了计数的呢?答案就在下面,我们接着往下看。


3.5 [ ]运算符重载

下面是运算符[]重载的函数原型:

在这里插入图片描述

  • mapped_type —> value
  • key_type —> key

简单来说[]的使用规则是传入key,返回对应value的引用。那要是传入的key在当前的map中没有呢?

如果map中没有key,则会插入一个由key和value(默认值)组成的键值对,最后返回刚插入的value的引用。

[]的调用等价于:

*((this->insert(make_pair(k,mapped_type()))).first)).second

是不是有点意外,[]的重载竟然不是复用find函数,而是复用insert函数。
[]重载实现的关键是利用了insert函数的返回值(前面想着map的函数接口和set差不多,打算偷懒不重复介绍了,看来前面欠的这里终归要还呜呜…),来看:

在这里插入图片描述
insert的返回值是一个迭代器,如果插入成功(map中原本没有key),则返回一个由指向新插入元素的迭代器和bool值(插入成功为true,失败为false) 组成的键值对;如果插入失败(map中原本有key),则返回一个由map中已存在元素的迭代器和bool值 组成的键值对。

然后.first访问到的是新插入的、或原本元素的迭代器,再解引用.second访问到的就是我们想要的key对应的value了。

头像

这里的逻辑有点绕,我大概画一下其中的指向关系:
在这里插入图片描述

简单总结就是:key存在,返回对应的value;key不存在,插入key和value(默认)。
看到这里相信我们就理解了上面调用[]重载计数的原理。

void test3()
{
	map<string, string> m;

	//插入
	m.insert(make_pair("front", "前"));

	//插入 <"behind", "">
	m["behind"];

	//插入+修改
	m["left"] = "左";

	//查找
	cout << m["right"] << endl;
}

虽然[]这样重载确实在某些场景方便了我们,但也存在不好的一面。比如我们原本想查找,但如果map中没有这个元素,那就会把这个元素插入进入。所以[]固然好用,但要谨慎使用哦!


4、multimap

4.1 multimap 介绍

  • multimap文档介绍
  • multimap也声明在头文件<map>

其实multimap也没什么可介绍的了,无非就是相比较与map,multimap允许键值冗余,再结合multisetmultimap无非也就那点东西喽。(被我逮到偷懒的时机了吧)
不过值得一说的是multimap没有再重载[],因为multmap的特点重载[]会使得其行为变得不明确。
还有就是从下面的实验可以得出multimapmultiset的erase函数接口特点是一样的。

void test4()
{
	multimap<string, string> mtm;
	mtm.insert({ "front", "前" });
	mtm.insert({ "behind", "后"});
	mtm.insert({ "left", "左" });
	mtm.insert({ "front", "前" });
	mtm.insert({ "behind", "后" });
	mtm.insert({ "front", "前" });
	mtm.insert({ "right", "右" });
	mtm.insert({ "left", "左" });
	mtm.insert({ "behind", "后" });
	mtm.insert({ "front", "前" });
	mtm.insert({ "left", "左" });

	for (const auto& e : mtm)
	{
		cout << e.first << "->" << e.second << endl;
	}
	cout << endl;

	auto it = mtm.find("front");
	//传迭代器删除
	mtm.erase(it);
	for (const auto& e : mtm)
	{
		cout << e.first << "->" << e.second << endl;
	}
	cout << endl;

	//传值删除
	mtm.erase("behind");
	for (const auto& e : mtm)
	{
		cout << e.first << "->" << e.second << endl;
	}
	cout << endl;

	//传迭代器区间删除
	mtm.erase(mtm.equal_range("left").first, mtm.equal_range("left").second);
	for (const auto& e : mtm)
	{
		cout << e.first << "->" << e.second << endl;
	}
	cout << endl;
}

请添加图片描述

如果multimap中存在多个key,则传迭代器只会删除一个,传值和传迭代器区间会将multimap中所有的key删除。


本篇文章的分享就到这里了,如果您觉得在本文有所收获,还请留下您的三连支持哦~

头像

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

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

相关文章

细说机房安装带孔的通风防静电地板的原因

静电在我们的日常生活中无处不在&#xff0c;但在机房等特殊环境中&#xff0c;静电却可能带来巨大的危害&#xff0c;为了防止静电带来的危害&#xff0c;很多机房都会安装防静电地板。其中有一部分机房会安装带孔的通风防静电地板&#xff0c;那么带孔的通风防静电地板有什么…

小程序-生命周期与WXS脚本

生命周期 什么是生命周期 生命周期&#xff08;Life Cycle&#xff09;是指一个对象从创建 -> 运行 -> 销毁的整个阶段&#xff0c;强调的是一个时间段。 我们可以把每个小程序运行的过程&#xff0c;也概括为生命周期&#xff1a; 小程序的启动&#xff0c;表示生命…

Java免税商品购物商城:Spring Boot实现详解

第一章 绪论 1.1 课题开发的背景 从古至今&#xff0c;通过书本获取知识信息的方式完全被互联网络信息化&#xff0c;但是免税商品优选购物商城&#xff0c;对于购物商城工作来说&#xff0c;仍然是一项非常重要的工作。尤其是免税商品优选购物商城&#xff0c;传统人工记录模式…

夹耳蓝牙耳机哪个品牌质量最好最耐用?2024年耳夹式耳机选购指南

随着科技的进步和人们对音质要求的提高&#xff0c;耳夹式耳机逐渐成为大家的耳机新宠。但是&#xff0c;面对市场上琳琅满目的耳夹式耳机品牌&#xff0c;许多人都会困惑&#xff1a;夹耳蓝牙耳机哪个品牌质量最好最耐用&#xff1f;其实&#xff0c;选对品牌不仅关系到音质的…

计算机前沿技术-人工智能算法-生成对抗网络-算法原理及应用实践

计算机前沿技术-人工智能算法-生成对抗网络-算法原理及应用实践 1. 什么是生成对抗网络&#xff1f; 生成对抗网络&#xff08;Generative Adversarial Networks&#xff0c;简称GANs&#xff09;是由Ian Goodfellow等人在2014年提出的一种深度学习模型&#xff0c;主要用于数…

OpenHarmony(鸿蒙南向)——平台驱动开发【PIN】

往期知识点记录&#xff1a; 鸿蒙&#xff08;HarmonyOS&#xff09;应用层开发&#xff08;北向&#xff09;知识点汇总 鸿蒙&#xff08;OpenHarmony&#xff09;南向开发保姆级知识点汇总~ 持续更新中…… 概述 功能简介 PIN即管脚控制器&#xff0c;用于统一管理各SoC的…

Python 在PDF中插入文本超链接和图片超链接 (详解)

目录 使用工具 Python给PDF添加网页链接 Python给PDF添加内部文件链接 Python给PDF添加外部文件链接 Python给PDF中现有文本添加超链接 Python在PDF中插入带超链接的图片 Python给PDF中现有图片添加超链接 超链接是指向特定资源&#xff08;如外部网页、文档内部位置或其…

Android个性名片界面的设计——约束布局的应用

节选自《Android应用开发项目式教程》&#xff0c;机械工业出版社&#xff0c;2024年7月出版 做最简单的安卓入门教程&#xff0c;手把手视频、代码、答疑全配齐 【任务目标】 使用约束布局、TextView控件实现一个个性名片界面的设计&#xff0c;界面如图1所示。 图1 个性名片…

跟王道学c记录

scanf int a; scanf("%d",&a); 一定要有取地址符 printf 用%f精度修饰符指定想要的小数位数。例如,%5.2f会至少显示5位数字并带有2位小 数的浮点数 用%s精度修饰符简单地表示一个最大的长度,以补充句点前的最小字段长度 printf 数的所有输出都是右对齐的,除非…

C++的哲学思想

C的哲学思想 文章目录 C的哲学思想&#x1f4a1;前言&#x1f4a1;C的哲学思想☁️C底层不应该基于任何其他语言&#xff08;汇编语言除外&#xff09;☁️只为使用的东西付费&#xff08;不需要为没有使用到的语言特性付费&#xff09;☁️以低成本提供高级抽象&#xff08;更…

exBase

1.准备工作 1.端口配置 下列为默认端口号&#xff0c;若部分端口号已被占用&#xff0c;用户可以根据实际情况进行修改。 端口号 说明 31030 exBase默认端口 31003 配置库默认端口 2181 zookeeper默认端口 9092 kafka默认端口 8091 metaNode的RPC端口 8092 node…

【Linux】进程管理:从理论到实践(一)

&#x1f308; 个人主页&#xff1a;Zfox_ &#x1f525; 系列专栏&#xff1a;Linux 目录 一&#xff1a; &#x1f525; 进程的基本概念 二&#xff1a; &#x1f525; 描述进程-PCB三&#xff1a; &#x1f525; 查看进程 &#x1f95d; 通过系统目录&#x1f95d; 通过ps命…

小程序视频编辑SDK解决方案,轻量化视频制作解决方案

面对小程序、网页、HTML5等多样化平台&#xff0c;如何轻松实现视频编辑的轻量化与高效化&#xff0c;成为了众多开发者和内容创作者共同面临的挑战。正是洞察到这一市场需求&#xff0c;美摄科技推出了其领先的小程序视频编辑SDK解决方案&#xff0c;为创意插上翅膀&#xff0…

线性代数(持续更新)

一.矩阵及其计算 1.矩阵的概念 矩阵就是一个数表 元素全是0&#xff0c;是零矩阵&#xff0c;用0来表示 当mn时&#xff0c;称为n阶矩阵&#xff08;方阵&#xff09; 只有一行的叫行矩阵&#xff0c;只有一列的叫列矩阵 只有对角线有元素的叫做对角矩阵&#xff0c;用dia…

FTP被动模式配置

FTP被动模式配置 非云服务器或未开启防火墙的服务器不需要设置 背景&#xff1a; 某些FTP客户端与FTP服务器进行数据交互时&#xff0c;客户端数据传输使用的是被动模式&#xff1b; 被动模式会导致服务端的数据通道端口随机变动&#xff0c;服务器的防火墙无法设置放行规则…

Excel数据检视——对角线连续数据连线

实例需求&#xff1a;数据表如下图所示&#xff0c;现需要根据规则&#xff0c;在符合要求的单元格上&#xff0c;添加连线。 连续单元格位于对角线方向单元格内容相同连续单元格数量不少于7个 示例代码如下。 Sub LT2RB()Dim objDic As Object, rngData As Range, bFlag As …

基于springBoot校园健康驿站管理平台(源码+教程)

互联网发展至今&#xff0c;无论是其理论还是技术都已经成熟&#xff0c;而且它广泛参与在社会中的方方面面。它让信息都可以通过网络传播&#xff0c;搭配信息管理工具可以很好地为人们提供服务。针对信息管理混乱&#xff0c;出错率高&#xff0c;信息安全性差&#xff0c;劳…

开源标注工具

DoTAT https://github.com/FXLP/MarkTool 后端代码未开放&#xff0c;可能有数据泄露风险 Chinese-Annotator https://github.com/deepwel/Chinese-Annotator 安装非常麻烦&#xff0c;github更新频率比较低&#xff0c;支持功能和doccano类似 IEPY https://github.com/ma…

头戴式蓝牙无线耳机哪个牌子比较好?头戴式蓝牙耳机排行榜盘点

在当今快节奏的生活中&#xff0c;音乐已成为我们不可或缺的精神食粮&#xff0c;而一款优秀的头戴式蓝牙无线耳机&#xff0c;不仅能为我们带来高品质的音频享受&#xff0c;还能让我们在繁忙的生活中找到片刻的宁静与放松&#xff0c;那么头戴式蓝牙无线耳机哪个牌子比较好&a…

【工具变量】数字技术应用广度与深度数据集(2001-2023年)

数据简介&#xff1a;数字技术应用广度是指企业为了实现收集、存储与处理海量数据时所需要的技术支撑&#xff0c;诸如大数据、云计算、区块链等等。数字技术应用深度包括数字化管理和数字化生产。其中&#xff0c;数字化管理是指企业应用数字技术实现组织、生产、销售和服务智…