【C++】数据结构的恶龙set和map来了~

news2024/11/29 3:43:22

下一篇AVL树难点中的难点~

 

文章目录

  • 前言
  • 一、set的介绍
  • 二、map的介绍
  •        题目练习
  • 总结

 


前言

1.关联式容器

在初阶阶段,我们已经接触过STL中的部分容器,比如:vector、list、deque、
forward_list(C++11)等,这些容器统称为序列式容器,因为其底层为线性序列的数据结构,里面
存储的是元素本身。那什么是关联式容器?它与序列式容器有什么区别?
关联式容器也是用来存储数据的,与序列式容器不同的是,其 里面存储的是 <key, value> 结构的
键值对,在数据检索时比序列式容器效率更高
 
2.键值对
 
用来表示具有一一对应关系的一种结构,该结构中一般只包含两个成员变量 key value key
表键值, value 表示与 key 对应的信息。比如:现在要建立一个英汉互译的字典,那该字典中必然
有英文单词与其对应的中文含义,而且,英文单词与其中文含义是一一对应的关系,即通过该应
该单词,在词典中就可以找到与其对应的中文含义。
 
3.树形结构的关联式容器
 
根据应用场景的不同,STL总共实现了两种不同结构的管理式容器:树型结构与哈希结构。 树型结
构的关联式容器主要有四种: map set multimap multiset。这四种容器的共同点是:使
用平衡搜索树(即红黑树)作为其底层结果,容器中的元素是一个有序的序列。下面一依次介绍每一
个容器。

 

一、set的介绍

1. set是按照一定次序存储元素的容器
2. 在set中,元素的value也标识它(value就是key,类型为T),并且每个value必须是唯一的。
set中的元素不能在容器中修改(元素总是const),但是可以从容器中插入或删除它们。
3. 在内部,set中的元素总是按照其内部比较对象(类型比较)所指示的特定严格弱排序准则进行
排序。
4. set容器通过key访问单个元素的速度通常比unordered_set容器慢,但它们允许根据顺序对
子集进行直接迭代。
5. set在底层是用二叉搜索树(红黑树)实现的。
 
set的注意事项:
 
1. 与map/multimap不同,map/multimap中存储的是真正的键值对<key, value>,set中只放
value,但在底层实际存放的是由<value, value>构成的键值对。
2. set中插入元素时,只需要插入value即可,不需要构造键值对。
3. set中的元素不可以重复(因此可以使用set进行去重)。
4. 使用set的迭代器遍历set中的元素,可以得到有序序列
5. set中的元素默认按照小于来比较。
6. set中查找某个元素,时间复杂度为:O(n)
7. set中的元素不允许修改(为什么?)
8. set中的底层使用二叉搜索树(红黑树)来实现
 
下面我们来使用一下set:
void test_set1()
{
	set<int> st;
	st.insert(1);
	st.insert(2);
	st.insert(3);
	st.insert(1);
	st.insert(3);
	st.insert(4);
	set<int>::iterator it = st.begin();
	while (it != st.end())
	{
		cout << *it << " ";
		++it;
	}
}
int main()
{
	test_set1();
	return 0;
}

set的使用与我们之前学的容器相差不大,大家只需要记住set的作用是排序+去重,当然set既然支持迭代器那么也肯定支持范围for了。

2d0877809fbd42aebd5fe6630ee11714.png

 在这里我们要注意,set是不支持修改里面的值的,因为一旦修改了就不能保持搜索二叉树的特性了。下面我们演示一下set中的find和count接口:

void test_set2()
{
	set<int> st;
	st.insert(1);
	st.insert(2);
	st.insert(3);
	st.insert(1);
	st.insert(3);
	st.insert(4);
	auto it = st.find(4);
	if (it != st.end())
	{
		cout << "要查询的值存在:" << *it << endl;
	}
	else
	{
		cout << "不存在" << endl;
	}
}
int main()
{
	//test_set1();
	test_set2();
	return 0;
}
void test_set2()
{
	set<int> st;
	st.insert(1);
	st.insert(2);
	st.insert(3);
	st.insert(1);
	st.insert(3);
	st.insert(4);
	if (st.count(4))
	{
		cout << "要查询的值存在" << endl;
	}
	else
	{
		cout << "不存在" << endl;
	}
}

在set中我们确认一个值用count接口会比find更方便,因为如果存在那么count一定是1,如果不存在则是0.count这个接口是为了和multiset中的count对应。

下面我们来看看multiset:

void test_set3()
{
	multiset<int> st;
	st.insert(1);
	st.insert(2);
	st.insert(3);
	st.insert(1);
	st.insert(3);
	st.insert(4);
	for (auto& e : st)
	{
		cout << e << " ";
	}
	
}

fbaafaf43dc74720af43b3577a07b28a.png

通过结果我们可以看到multiset的功能仅仅是排序,并不会去重,下面我们看看刚刚说到的multiset中的count:

void test_set3()
{
	multiset<int> st;
	st.insert(1);
	st.insert(2);
	st.insert(3);
	st.insert(1);
	st.insert(3);
	st.insert(4);
	for (auto& e : st)
	{
		auto it = st.find(e);
		if (it != st.end())
		{
			cout << e << "的个数为:" << st.count(e) << endl;
		}
		else
		{
			cout << "找不到" << endl;
		}
	}
	
}

78368d60a0e64e339d0b6d80c94068eb.png

 我们可以看到在multiset中count可以准确的计算元素的数量,下面我们看看find接口:

void test_set3()
{
	multiset<int> st;
	st.insert(1);
	st.insert(1);
	st.insert(1);
	st.insert(1);
	st.insert(1);
	st.insert(1);
	auto it = st.find(1);
	while (it != st.end()&&*it == 1)
	{
		cout << *it << " ";
		++it;
	}
}

c679a823907f47eeadbecc9001172f3a.png

 对于find接口来讲,查找到的元素一定是中序遍历的第一个元素,如果不是这样的原则我们上面的打印是不会将所有1打印出来的。

set的其他接口我们就不再演示了,因为与其他STL容易相差并不大,只需要记住几个有差异的接口就可以了。

二、map的介绍

1. map是关联容器,它按照特定的次序(按照key来比较)存储由键值key和值value组合而成的元
素。
2. 在map中,键值key通常用于排序和惟一地标识元素,而值value中存储与此键值key关联的
内容。键值key和值value的类型可能不同,并且在map的内部,key与value通过成员类型
value_type绑定在一起,为其取别名称为pair:
typedef pair<const key, T> value_type;
3. 在内部,map中的元素总是按照键值key进行比较排序的。
4. map中通过键值访问单个元素的速度通常比unordered_map容器慢,但map允许根据顺序
对元素进行直接迭代(即对map中的元素进行迭代时,可以得到一个有序的序列)。
5. map支持下标访问符,即在[]中放入key,就可以找到与key对应的value。
6. map通常被实现为二叉搜索树(更准确的说:平衡二叉搜索树(红黑树))

bb1f18b5d2b94878a084e0c05ade4d5d.png

 我们前面已经说过了,map里面存的是键值对,下面我们演示一下map的使用:

void test_map1()
{
	map<string, string> mp;
	mp.insert(make_pair("string", "字符串"));
	mp.insert(make_pair("left", "左边"));
	mp.insert(make_pair("right", "右边"));
	mp.insert(make_pair("erase", "删除"));
	mp.insert(make_pair("string", "字符串2"));
	auto it = mp.begin();
	while (it != mp.end())
	{
		cout << (*it).first << ":" << (*it).second << endl;
		++it;
	}
}

41afb17b69104296b4e908e7955d4466.png

 我们可以看到map同样是排序+去重,当然如果看不懂make_pair是什么那么我们写的再原始一点:

eb9042822fe546d9b1f1f6bf7d6bfe43.png

 如上图所示绿色的是原始的pair插入,对于打印为什么是first和second呢?因为pair这个结构里存的就是K,V键值对,并且first代表key,second代表value.当然还记得我们在写反向迭代器的时候重载的那个->符号吗,在这里也有哦:

af4af66ac6484653b09c90e4ae8cb89e.png

 在这里->本来要用两次,因为首先访问到结构体,其次再访问到结构体里的first或者second,但是现在一个->就搞定了因为重载了->符号。下面我们讲一下在map中最重要的[]符号:

void test_map2()
{
	map<string, int> mp;
	string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜","苹果", "香蕉", 
		"苹果", "香蕉" };
	for (auto& e : arr)
	{
		auto it = mp.find(e);
		if (it == mp.end())
		{
			mp.insert(make_pair(e, 1));
		}
		else
		{
			it->second++;
		}
	}
	for (auto& m : mp)
	{
		cout << m.first << ":" << m.second << endl;
	}
}

34e230fb1cf5432d9b6b1127fb4211f7.png

 还记得我们学二叉搜索树的时候用的水果的例子吗?当我们树中已经存在这个水果了我们就让水果的count++,如果不存在这个水果我们就让插入并且count值为1,如今有了方括号我们1行代码就能搞定:

void test_map2()
{
	map<string, int> mp;
	string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜","苹果", "香蕉", 
		"苹果", "香蕉" };
	for (auto& e : arr)
	{
		mp[e]++;
	}
	for (auto& m : mp)
	{
		cout << m.first << ":" << m.second << endl;
	}
}

beddc1ebf81c41b783150c8431649280.png

 是不是很神奇呢?下面我们来重点介绍一个[]运算符:

4a5088a49437459daf0ed30c5c57367d.png

 从文档中我们可以知道[]有多个作用,首先我们要看懂[]的返回值,如果要插入的值在当前树中不存在我们就插入,如果存在(如何判断是否存在是因为插入有一个bool值)就返回second的值(准确来说是引用),这也就解释了我们刚刚那个代码,因为当要插入的水果不存在的时候就直接帮我们插入了,并且插入后返回了此种水果的次数,然后我们后置++让这个水果变成了1次,后面如果发现这种水果已经被插入了就不在插入了但是返回值返回的是水果的次数的引用所以我们会再次++让次数变成2,然后重复。也就是说[]有四个作用:1.插入  2.修改  3.插入+修改  4.查找

void test_map1()
{
	map<string, string> mp;
	mp.insert(make_pair("string", "字符串"));
	mp.insert(make_pair("right", "右边"));
	mp.insert(make_pair("erase", "删除"));
	mp["left"];   //仅仅插入
	mp["second"] = "第二";   //插入加修改 因为返回值是second,我们将默认的second修改为"第二"
	mp["string"] = "字符串2";   //修改
	cout << mp["string"] << endl;  //查找
	auto it = mp.begin();
	while (it != mp.end())
	{
		cout << it->first << ":" << it->second << endl;
		++it;
	}
}

adf28cbdf3df423b9d1a6ae57d528852.png

如果我们仅仅想要用[]插入,这个时候插入的是key模型,value会使用缺省值,比如我们方面的插入left,最后打印发现left的value值为空,这就可以验证当value的类型为字符串时缺省值为空字符串,当我们想要插入加修改时,因为[]的返回值为value的引用,所以相当于我们把缺省值修改为自己想要的value值,如果仅仅要对某个key的value值修改我们可以借助[]的返回值进行修改,以上就是map的一些知识,下面我们讲解multimap的知识:

1. Multimaps是关联式容器,它按照特定的顺序,存储由key和value映射成的键值对<key,
value>,其中多个键值对之间的key是可以重复的。
2. 在multimap中,通常按照key排序和惟一地标识元素,而映射的value存储与key关联的内
容。key和value的类型可能不同,通过multimap内部的成员类型value_type组合在一起,
value_type是组合key和value的键值对: typedef pair<const Key, T> value_type;
3. 在内部,multimap中的元素总是通过其内部比较对象,按照指定的特定严格弱排序标准对
key进行排序的。
4. multimap通过key访问单个元素的速度通常比unordered_multimap容器慢,但是使用迭代
器直接遍历multimap中的元素可以得到关于key有序的序列。
5. multimap在底层用二叉搜索树(红黑树)来实现。
注意: multimap map 的唯一不同就是: map 中的 key 是唯一的,而 multimap key 是可以
重复的

782b622feb5046119506bf39417062c4.png

 multimap与multiset一样,都是支持键值冗余,也可以理解为仅仅排序不去重,与map不同的是,由于multimap的键值不再是一一对应的关系了,所以multimap没有了[]运算符重载。下面我们演示一下:

void test_map1()
{
	multimap<string, string> mp;
	mp.insert(make_pair("string", "字符串1"));
	mp.insert(make_pair("string", "字符串2"));
	mp.insert(make_pair("string", "字符串3"));
	mp.insert(make_pair("left", "左边"));
	mp.insert(make_pair("right", "右边"));
	for (auto& e : mp)
	{
		cout << e.first << ":" << e.second << endl;
	}
}

abfae04bc94e479d99ec430160f59bbc.png

 当然multimap虽然没有了[]运算符,但是count还是比较有用的:

void test_map1()
{
	multimap<string, string> mp;
	mp.insert(make_pair("string", "字符串1"));
	mp.insert(make_pair("string", "字符串2"));
	mp.insert(make_pair("string", "字符串3"));
	mp.insert(make_pair("left", "左边"));
	mp.insert(make_pair("right", "右边"));
	if (mp.count("string"))
	{
		cout << "string的个数:" << mp.count("string") << endl;
	}
}

91eb9a341a4d413a9f1413f8146afe01.png

 在multimap和multiset中count都可以计算出某个key的个数,以上就是map和set的基本用法以及一些重要功能的讲解,下面我们讲一道题练习一下map和set。

三.题目练习

力扣:前K个高频单词

力扣链接:力扣

915c1d3ddd7240c0b9cbbd7d575df8c0.png

 看到这道题大家应该首先想到的就是map了吧,毕竟题目已经很明显的说了KV键值对,下面我们说一下思路:首先我们用mp的[]功能统计出每个单词出现的次数以及对应的每个单词,然后我们将前K个这样的单词放入向量中,然后打印即可:

class Solution {
public:
    struct compare
    {
        bool operator()(const pair<string,int>& k1,const pair<string,int>& k2)
        {
            return k1.second>k2.second;
        }
    };
    vector<string> topKFrequent(vector<string>& words, int k) {
        map<string,int> mp;
        for (auto& e:words)
        {
            mp[e]++;
        }
        vector<pair<string,int>> v(mp.begin(),mp.end());
        sort(v.begin(),v.end(),compare());
        vector<string> result;
        for (int i = 0;i<k;i++)
        {
            result.push_back(v[i].first);
        }
        return result;
    }
};

首先我们用一个map来计算单词以及单词的次数,然后用一个vector来保存这些键值对(vector的类型一定是pair),然后我们用sort函数排序vector,由于pair默认的排序方式是以K值进行排序的,所以我们必须写一个仿函数来用键值对中的value值比较,比较完成后我们用另一个vector来接收前K个字符串,最后我们只需要返回string即可所以是v[i].first。但是如果我们发现没有通过,如下图:

1758f2ffb3d7423aa155435f5c629c5b.png

 发现错误的原因是单词的顺序不对,当出现相同次数的单词需要用字典序来排序,所以这个时候我们就想到了排序的稳定性,如果一个排序稳定的话相同的值的相对顺序是不变的,本题给的测试列表本来也是按照字典序排好的,所以我们直接换一个稳定排序就解决了,在算法中有一个稳定排序:

f650757d2acb4d0a97e7aa788308773c.png

 下面我们就换上这个稳定排序:

223107920ebd4f21a7e81274ae7f21b3.png

 这样我们就通过了,那么如果我们还有其他办法解决刚刚的顺序问题吗?答案是当然有,要知道我们写仿函数的目的就是为了控制比较方式:

70341f4f730a4154a27e4c862b210ae6.png

 当然我们也可以用我们刚刚学的的set来进行比较,因为我们的set和map也是支持控制比较方式的:

class Solution {
public:
    struct compare
    {
        bool operator()(const pair<string,int>& k1,const pair<string,int>& k2) const
        {
            return k1.second>k2.second||(k1.second==k2.second)&&k1.first<k2.first;
        }
    };
    vector<string> topKFrequent(vector<string>& words, int k) {
        map<string,int> mp;
        for (auto& e:words)
        {
            mp[e]++;
        }
        set<pair<string,int>,compare> st(mp.begin(),mp.end());
        vector<string> v;
        auto it = st.begin();
        while (k--)
        {
            v.push_back(it->first);
            ++it;
        }
        return v;
    }
};

在这里大家要注意:

4e44fb0d340540589c113209a8ad88ae.png

 set中对于仿函数用的是模板,模板我们必须传类型所以我们写set的时候写的compare,像sort就不一样了:

22da5cf5220d4b2f952deb0f3885e43d.png

我们可以看到sort中传的是仿函数对象,所以我们使用的时候用了匿名对象compare().当然上面的代码中我们写仿函数一定要在后面加上const,因为有些测试用例会有const对象,如果没写const会编译报错。


 

总结

学数据结构就如同我们打怪升级一般,从一开始的单链表到二叉树,每一次的难度都是层层递进的,到了map这边下一篇我们要讲的AVL树难度要比二叉树搜索树难了不止一点,但是只要大家有着面对困难打败困难的心就一定可以成为屠龙勇士~

 

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

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

相关文章

vue3【抛弃vuex,使用pinia】

Pinia Pinia 中文文档 一个全新的用于Vue的状态管理库下一个版本的vuex&#xff0c;也就是vuex5.0vue2 和vue3都支持在vue2中pinia使用vuex的现有接口&#xff0c;所以不能与vuex一起使用相比vuex有更完美的TypeScript支持支持服务器端渲染 Pinia核心概念 Pinia 从使用角度和…

【论文笔记】Learning Latent Dynamics for Planning from Pixels

论文及代码解读&#xff1a;Learning Latent Dynamics for Planning from Pixels 文章目录 论文及代码解读&#xff1a;Learning Latent Dynamics for Planning from Pixels3. Recurrent State Space ModelLatent dynamicsVariational encoderTraining objectiveDeterministic …

Linux系统编程学习 NO.2 ——环境配置和基础指令的学习

操作系统根据使用方式分类 操作系统按照使用类型可分为图形化操作系统和指令操作系统。图形化操作系统的本质其实也是根据指令来操作的。指令更贴近操作系统的底层。而我在学习Linux系统编程时&#xff0c;采用命令行的方式来学习。 补充一个小知识&#xff1a;早期命令行操作…

原装RS罗德与施瓦茨FSW85、FSW50,FSW67信号+频谱分析仪

Rohde & Schwarz FSW85 2Hz至85GHz信号和频谱分析仪 特征 10 kHz 偏移&#xff08;1 GHz 载波&#xff09;时的低相位噪声为 –137 dBc (1 Hz) WCDMA ACLR 测量的 -88 dBc 动态范围&#xff08;带噪声消除&#xff09; 高达 5 GHz 的分析带宽 < 0.4 dB 总测量不确定度高…

投资回报率业内最高!FortiGate在CyberRatings防火墙独立测试中收获近乎完美表现

对于需参考客观产品数据以制定明智采购决策的企业 IT 采购方而言&#xff0c;公正的第三方测试数据不失为一项关键参考。幸运的是&#xff0c;国际第三方安全测评机构CyberRatings始终秉持公平公正的独立测试理念&#xff0c;致力于量化网络安全风险&#xff0c;为多种网络安全…

用gost实现远程端口映射

gost 是一个非常优秀的tunnel. 支持多种形式的端口映射。 本文只介绍远程端口映射方式的tunnel. 远程端口映射的意思就是&#xff0c;将本地端的某个服务的端口A&#xff08;tcp/udp&#xff09;映射到远程的某个端口P上&#xff0c; 用户通过访问远程的端口P来访问本地端的这…

Linux多路转接之poll

文章目录 一、poll的认识二、编写poll方案服务器三、poll方案多路转接的总结 一、poll的认识 多路转接技术是在不断更新进步的&#xff0c;一开始多路转接采用的是select方案&#xff0c;但是select方案存在的缺点比较多&#xff0c;所以在此基础上改进&#xff0c;产生了poll…

怎么缩小照片的kb,压缩照片kb的几种方法

缩小照片的KB大小是我们日常工作生活中遇到的常见问题。虽然听起来十分专业&#xff0c;但其实很简单。照片的KB是指照片文件的大小&#xff0c;通常以“KB”为单位表示。缩小照片的KB就是减小照片文件的大小&#xff0c;以便占用更少的磁盘空间或更快地上传和下载照片。在实际…

什么是BI ?BI 能给企业带来什么价值?

目前&#xff0c;社会数字化程度还在不断加深&#xff0c;数据量也伴随着一同高速增长&#xff0c;许多人预测未来将是数据处理时代&#xff0c;而作为数据类解决方案的商业智能BI也会持续扩张市场&#xff0c;朝着不同行业BI商业智能的方向发展。 利用BI工具系统&#xff0c;…

MySQL 的 varchar 存储原理:InnoDB 记录存储结构

1. InnoDB 是干嘛的&#xff1f; InnoDB 是一个将表中的数据存储到磁盘上的存储引擎。 2. InnoDB 是如何读写数据的&#xff1f; InnoDB 处理数据的过程是发生在内存中的&#xff0c;需要把磁盘中的数据加载到内存中&#xff0c;如果是处理写入或修改请求的话&#xff0c;还…

统计学01: 中心极限定律、正态分布、z-score

<~生~信~交~流~与~合~作~请~关~注~公~众~号生信探索> 中心极限定律 中心极限定律&#xff1a;当样本样足够大时&#xff08;n≥30&#xff09;&#xff0c;样本的mean等于总体的mean 例如&#xff0c;对学校的学生身高抽样&#xff0c;100组每组30人&#xff0c;每组的身…

JavaScript沙箱

1、什么是沙箱 在计算机安全中&#xff0c;沙箱&#xff08;Sandbox&#xff09;是一种用于隔离正在运行程序的安全机制&#xff0c;通常用于执行未经测试或者不受信任的程序或代码&#xff0c;它会为待执行的程序创建一个独立的执行环境&#xff0c;内部程序的执行不会影响到…

【JOSE约瑟 JZS-7E14/11静态可调延时中间继电器 自动控制电路 接通、分断电路】

JZS-7E14/11静态可调延时中间继电器品牌:JOSEF约瑟名称:静态可调延时中间继电器型号:JZS-7E14/11额定电压:6220VDC&#xff1b;6380VAC触点容量:10A/250V10A/220VDC功率消耗:≤6W 一 用途 JZS-7E系列中间继电器用于各种保护和自动控制装置中,以增加保护和控制回路的触点容量. …

Java面试知识点(全)-数据结构和算法

Java面试知识点(全) 导航&#xff1a; https://nanxiang.blog.csdn.net/article/details/130640392 注&#xff1a;随时更新 基础的数据结构 数组 数组的下标寻址十分迅速&#xff0c;但计算机的内存是有限的&#xff0c;故数组的长度也是有限的&#xff0c;实际应用当中的数据…

伙伴云CEO戴志康:低代码与GPT,是赛车手和领航员的角色

GPT来的突然&#xff0c;不仅打了那些对AI冷眼相待的人们一个措手不及&#xff0c;也顺势带动了全民”AIGC”讨论热潮&#xff0c;让大众开始期待它的到来&#xff0c;能为这个人间添上多少精彩.... 万众期待下&#xff0c;GPT也没谦虚&#xff0c;大笔一挥间便融入了到了协同办…

Java集合常见面试题

1、Java集合概述 Java集合&#xff0c;也叫作容器。由两大接口派生而来&#xff1a;Collection接口&#xff0c;用于存放单一元素&#xff1b;Map接口&#xff0c;主要用于存放键值对。对于Collection接口&#xff0c;下面又有三个主要的子接口&#xff1a;List、Set、Queue 2…

桌面远程工具推荐

目前市面上的远程工具多如牛毛&#xff0c;很多人不知道怎么选择&#xff0c;下面小编介绍两种桌面远程工具&#xff0c;它们都是跨平台的&#xff0c;均支持Windows&#xff0c;Mac OS&#xff0c;IOS和安卓&#xff0c;分别是RayLink&#xff0c;VNC&#xff0c;好用&#xf…

eKuiper 源码解读:从一条 SQL 到流处理任务的旅程

概述 LF Edge eKuiper 是 Golang 实现的轻量级物联网边缘分析、流式处理开源软件&#xff0c;可以运行在各类资源受限的边缘设备上。eKuiper 的主要目标是在边缘端提供一个流媒体软件框架。其规则引擎允许用户提供基于SQL 或基于图形&#xff08;类似于 Node-RED&#xff09;的…

权威硬核认证|数说故事携手IDEA共创学术论文获NLP国际顶会 ACL 2023收录

日前&#xff0c;数说故事携手IDEA共创的学术论文——《A Unified One-Step Solution for Aspect Sentiment Quad Prediction (一个统一的单步情感四元组识别方法) 》被国际学术顶会 ACL 2023 接收为 Findings长文。这是继上一年IDEA数说故事实验室论文获「国际AI顶会IJCAI-ECA…

加密解密软件VMProtect教程(六):主窗口之控制面板“项目”部分(1)

VMProtect 是保护应用程序代码免遭分析和破解的可靠工具&#xff0c;但只有在正确构建应用程序内保护机制并且没有可能破坏整个保护的典型错误的情况下才能最有效地使用。 接下来为大家介绍关于VMProtect主窗口中的控制面板&#xff0c;其中包括&#xff1a;“项目”部分、“功…