🌈欢迎来到C++专栏~~Map 和Set
- (꒪ꇴ꒪(꒪ꇴ꒪ )🐣,我是Scort
- 目前状态:大三非科班啃C++中
- 🌍博客主页:张小姐的猫~江湖背景
- 快上车🚘,握好方向盘跟我有一起打天下嘞!
- 送给自己的一句鸡汤🤔:
- 🔥真正的大师永远怀着一颗学徒的心
- 作者水平很有限,如果发现错误,可在评论区指正,感谢🙏
- 🎉🎉欢迎持续关注!

Map 和Set

在初阶阶段,我们已经接触过STL中的部分容器,比如:vector、list、deque、
forward_list(C++11)等,这些容器统称为序列式容器,因为其底层为线性序列的数据结构,里面存储的是元素本身。那什么是关联式容器?它与序列式容器有什么区别?
关联式容器也是用来存储数据的,与序列式容器不同的是,其里面存储的是<key, value>结构的键值对,在数据检索时比序列式容器效率更高
| 关联式容器 | 容器结构 | 底层实现 |
|---|---|---|
| set、map、multiset、multimap | 树型结构 | 平衡搜索树(红黑树) |
| unordered_set、unordered_map、unordered_multiset、unordered_multimap | 哈希结构 | 哈希表,哈希桶 |
用来表示具有一一对应关系的一种结构,该结构中一般只包含两个成员变量key和value,key代表键值,value表示与key对应的信息
举例子:
SGI-STL中关于键值对pair的定义
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本质就是Key的模型,就是确认该值在不在
Set支持的操作是增删查,不能改!(因为底层是一颗搜索树,改了就乱了)

三个模板参数:
T:set中存放元素的类型,实际在底层存储<value, value>的键值对Compare:仿函数;set中元素默认按照小于来比较(如果比较的方式不满意,可以自己去控制比较规则:自己写仿函数)Alloc:set中元素空间的管理方式,使用STL提供的空间配置器 ( 内存池)注意:在使用set时,需要包含头文件set

拷贝都是深拷贝,要拷贝一棵树,代价很大
void testset2()
{
set<int> set1;//空构造
int num[] = { 1, 2, 3, 7, 9, 3, 10, 1 };
//区间构造
set<int> set2(num, num + sizeof(num) / sizeof(num[0]));
//拷贝构造
set<int> set3(set2);
for (auto& e : set3) //打印结果看出set可以去重
{
cout << e << " ";
}
cout << endl;
}

此处和之前的容器差不多,所以就省略过,直接看图
| 函数说明 | 功能介绍 |
|---|---|
| begin + end(重点) | 获取第一个数据位置的, 获取最后一个数据的下一个位置的 |
| cbegin() const 和 cend()const | 无非就是针对const的版本,不可写 |
| rbegin + rend | 获取最后一个数据位置,获取第一个数据前一个位置 |
| 反向迭代器 | 同理 |

void test_set()
{
//set<int> s;
//s.insert(1);
//s.insert(2);
//s.insert(3);
set<int> s = { 1, 2, 1, 6, 3, 8, 5 };
//排序 + 去重
set<int>::iterator it = s.begin();
while (it != s.end())
{
cout << *it << " ";
++it;
}
cout << endl;
//支持迭代器也就支持范围for了
for (auto e : s)
{
cout << e << " ";
}
cout << endl;
}

如果我要整成降序的呢?
greater int a[] = { 1, 2, 1, 6, 3, 8, 5 };
set<int, greater<int>> s(a, a + sizeof(a) / sizeof(int));
🥑find & erase

普通的寻找和删除当然没有什么问题!
但是特殊的呢?

所以写完find要判断一下是否真正的找到了,在才删除
set<int>::iterator pos = s.find(20);
if (pos != s.end())
{
s.erase(pos);
}
🥑count


但是count不是为此设计的!是为了和multiset保持接口的一致性,所以都设计了
提一嘴
🥑lower_bound 和 upper_bound
我想把某个范围的值都找到!
lower_bound :下限的意思,返回 >= val的值upper_bound:上限的意思,返回 > val的值 itlow = myset.lower_bound(35);//返回大于等于35的值
itup = myset.upper_bound(60);//返回大于60的值

multiset容器与set容器实现和接口基本一致,唯一区别就是,multiset 允许键值冗余,即multiset容器当中存储的元素是可以重复的
代码展示:
void testset4()
{
int a[] = { 1, 2, 1, 6, 3, 8, 5 };
//允许键值冗余
multiset<int> s(a, a + sizeof(a) / sizeof(int));
for (auto e : s)
{
cout << e << " ";
}
cout << endl;
}
结果展示:

键值冗余之后,我们的find该怎么样找数据呢?
void testset4()
{
int a[] = { 1, 2, 1, 6, 3, 8, 5, 3};
//允许键值冗余
multiset<int> s(a, a + sizeof(a) / sizeof(int));
//find时,如果有多个值,返回中序的第一个
auto pos = s.find(3);
while (pos != s.end())
{
cout << *pos << " ";
++pos;//这里的++ ,就是加到下一个的3的位置
}
cout << endl;
}

当然如果是要删除erase3的时候,肯定是删除所有的3!
typedef pair<const key, T> value_type;[]中放入key,就可以找到与key对应的value。
参数说明:
key: 键值对中key的类型
T: 键值对中value的类型
Compare: 比较器的类型,map中的元素是按照key来比较的,缺省情况下按照小于来比较,一般情况下(内置类型元素)该参数不需要传递,如果无法比较时(自定义类型),需要用户自己显式传递比较规则(一般情况下按照函数指针或者仿函数来传递)
Alloc:通过空间配置器来申请底层空间,不需要用户传递,除非用户不想使用标准库提供的空间配置器
使用map要包含map的头文件哦
//map<string, string>::iterator it = dict.begin();
auto it = dict.begin();
while (it != dict.end())
{
//pair没有重载流插入
//cout << (*it).first << (*it).second << endl;
//cout << it->->first << it->->second << endl;
//operator重载
cout << it->first << it->second << endl;
++it;
}
for (const auto& kv : dict)//给const & 引用
{
cout << kv.first << ":" << kv.second << endl;
}
cout << endl;

void testmap1()
{
map<int, int> map1;//空构造
int num[] = { 1,5,9,4,8,2,3,1,5,4,5,7 };
for (auto e : num)
{
map1.insert(make_pair(e,e));
}
map<int, int> map2(map1.begin(),map1.end());//迭代区间构造
map<int, int> map3(map2);//拷贝构造
for (auto& e : map3)
{
cout << e.first << ":" << e.second << endl;
}
}
🌏Insert

find插入的是pair的结构体,此版本是不支持冗余的,插入只看key值,有相等的就不需要了
void test_map1()
{
map<string, string> dict;
//pair<string, string> kv1("sort", "排序");
//dict.insert(kv1);
//匿名对象
dict.insert(pair<string, string>("sort", "排序"));
dict.insert(pair<string, string>("test", "测试"));
dict.insert(pair<string, string>("string", "字符串"));
//宏替换
typedef pair<string, string> Dictkv;
dict.insert(Dictkv("string", "字符串"));
}
还有一个很便捷的操作:make_pair:构造匿名的pair,返回
dict.insert(make_pair("string", "字符串"));//很常用


[]:的功能:查找 + 修改
key,就返回value的引用(查找 + 修改)pair(key, K()),会插入这个key值,value就要去调用默认构造,最后还是返回value的引用 (插入+ 修改)如果是int,就是0;是指针,就默认空指针;
map<string, string> dict;
dict.insert(make_pair("sort", "排序"));
dict["insert"]; //插入
dict["insert"] = "插入"; //插入 + 修改value返回值
dict["left"] = "左边"; //插入 + 修改

可以看见Insert的返回值和实现

key已经在map中,返回pair(key_iterator,false)key不在map中,返回pair(new_key_iterator,true)[]其底层实现:有两个pair结构
pair<iterator, bool>;第一个值是迭代器,第二个值是bool;第二个bool是用来反应是否插入成功,如果成功,则 第一个值迭代器就是指向插入的pair数据V& operator[](const K& key)
{
pair<iterator, bool> ret = insert(make_pair(key, V());
//返回key节点的迭代器,迭代器是map的,再调用->就是pair*,取second
return ret.first->second;
}
make_pair,使得count = 1++一次//统计次数
void test_map2()
{
string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜","苹果", "香蕉", "苹果", "香蕉" };
map<string, int> countMap;
for (auto& str : arr)
{
map<string, int>::iterator it = countMap.find(str);
if (it != countMap.end())
{
/*(*it).second++;*/
//it->->second++; 第一个箭头是调用operator->返回的是pair*,再->指向成员
it->second++;
}
else
{
//找不到
countMap.insert(make_pair(str, 1));
}
}
//遍历
map<string, int>::iterator it = countMap.begin();
while(it != countMap.end())
{
cout << it->first << ":" << it->second << endl;
++it;
}
cout << endl;
}

第二种方法:[]
map<string, int> countMap;
for (auto& str : arr)
{
//1、str不在countMap中,插入pair(str, int()),然后对返回次数++
//2、str在的话,就返回value的引用次数++
countMap[str]++;
}

multimap容器与map容器的底层实现以及成员函数的接口都是基本一致,区别是multimap 允许键值冗余,即multimap容器当中存储的元素是可以重复的
multimap<string, string> mdict;
mdict.insert(make_pair("sort", "排序"));
mdict.insert(make_pair("left", "左边"));
mdict.insert(make_pair("left", "左边"));
mdict.insert(make_pair("left", "你猜"));

题目地址:传送

要注意要按出现次数按大到小的来,如果出现次数相等,要按字母顺序排序,比如:i和love都出现了两次,那么 按字母顺序i要在love之前
思路:
countMap统计各种单词的出现的次数(统计次数 此时是i在上面,love在下面)sortMap按照字符出现的次数进行降序排列
注意不能用反向迭代来进行,不然同样的次数,love会在前面,i在后;就违背了题意
class Solution {
public:
vector<string> topKFrequent(vector<string>& words, int k) {
map<string, int> countMap;
//统计次数 此时是i在上面,love在下面
for(auto& str : words)
{
countMap[str]++;
}
// apple 3 \ i 2 \ love 2 \ j 5
multimap<int, string, greater<int>> sortMap;//排降序
for(auto& kv : countMap)
{
sortMap.insert(make_pair(kv.second, kv.first));
}
vector<string> v;
multimap<int, string, greater<int>> :: iterator it = sortMap.begin();
for(size_t i = 0; i < k; i++)
{
v.push_back(it->second);
it++;
}
return v;
}
};
题目地址:传送

思路:

class Solution {
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
set<int> s1 (nums1.begin(), nums1.end());
set<int> s2 (nums2.begin(), nums2.end());
set<int> :: iterator it1 = s1.begin();
auto it2 = s2.begin();
vector<int> v;
while(it1 != s1.end() && it2 != s2.end())
{
if(*it1 < *it2)
{
++it1;
}
else if(*it1 > *it2)
{
++it2;
}
else
{
v.push_back(*it1);
++it1;
++it2;
}
}
return v;
}
};
那如果是求差集呢?


我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h
我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i
我正在查看instance_variable_set的文档并看到给出的示例代码是这样做的:obj.instance_variable_set(:@instnc_var,"valuefortheinstancevariable")然后允许您在类的任何实例方法中以@instnc_var的形式访问该变量。我想知道为什么在@instnc_var之前需要一个冒号:。冒号有什么作用? 最佳答案 我的第一直觉是告诉你不要使用instance_variable_set除非你真的知道你用它做什么。它本质上是一种元编程工具或绕过实例变量可见性的黑客攻击
有时我需要处理键/值数据。我不喜欢使用数组,因为它们在大小上没有限制(很容易不小心添加超过2个项目,而且您最终需要稍后验证大小)。此外,0和1的索引变成了魔数(MagicNumber),并且在传达含义方面做得很差(“当我说0时,我的意思是head...”)。散列也不合适,因为可能会不小心添加额外的条目。我写了下面的类来解决这个问题:classPairattr_accessor:head,:taildefinitialize(h,t)@head,@tail=h,tendend它工作得很好并且解决了问题,但我很想知道:Ruby标准库是否已经带有这样一个类? 最佳
给定一个复杂的对象层次结构,幸运的是它不包含循环引用,我如何实现支持各种格式的序列化?我不是来讨论实际实现的。相反,我正在寻找可能会派上用场的设计模式提示。更准确地说:我正在使用Ruby,我想解析XML和JSON数据以构建复杂的对象层次结构。此外,应该可以将该层次结构序列化为JSON、XML和可能的HTML。我可以为此使用Builder模式吗?在任何提到的情况下,我都有某种结构化数据-无论是在内存中还是文本中-我想用它来构建其他东西。我认为将序列化逻辑与实际业务逻辑分开会很好,这样我以后就可以轻松支持多种XML格式。 最佳答案 我最
我不知道为什么,但是当我设置这个设置时它无法编译设置:static_cache_control,[:public,:max_age=>300]这是我得到的syntaxerror,unexpectedtASSOC,expecting']'(SyntaxError)set:static_cache_control,[:public,:max_age=>300]^我只想将“过期”header设置为css、javaascript和图像文件。谢谢。 最佳答案 我猜您使用的是Ruby1.8.7。Sinatra文档中显示的语法似乎是在Ruby1.
我正在尝试使用Curbgem执行以下POST以解析云curl-XPOST\-H"X-Parse-Application-Id:PARSE_APP_ID"\-H"X-Parse-REST-API-Key:PARSE_API_KEY"\-H"Content-Type:image/jpeg"\--data-binary'@myPicture.jpg'\https://api.parse.com/1/files/pic.jpg用这个:curl=Curl::Easy.new("https://api.parse.com/1/files/lion.jpg")curl.multipart_form_
无论您是想搭建桌面端、WEB端或者移动端APP应用,HOOPSPlatform组件都可以为您提供弹性的3D集成架构,同时,由工业领域3D技术专家组成的HOOPS技术团队也能为您提供技术支持服务。如果您的客户期望有一种在多个平台(桌面/WEB/APP,而且某些客户端是“瘦”客户端)快速、方便地将数据接入到3D应用系统的解决方案,并且当访问数据时,在各个平台上的性能和用户体验保持一致,HOOPSPlatform将帮助您完成。利用HOOPSPlatform,您可以开发在任何环境下的3D基础应用架构。HOOPSPlatform可以帮您打造3D创新型产品,HOOPSSDK包含的技术有:快速且准确的CAD
本教程将在Unity3D中混合Optitrack与数据手套的数据流,在人体运动的基础上,添加双手手指部分的运动。双手手背的角度仍由Optitrack提供,数据手套提供双手手指的角度。 01 客户端软件分别安装MotiveBody与MotionVenus并校准人体与数据手套。MotiveBodyMotionVenus数据手套使用、校准流程参照:https://gitee.com/foheart_1/foheart-h1-data-summary.git02 数据转发打开MotiveBody软件的Streaming,开始向Unity3D广播数据;MotionVenus中设置->选项选择Unit
文章目录一、概述简介原理模块二、配置Mysql使用版本环境要求1.操作系统2.mysql要求三、配置canal-server离线下载在线下载上传解压修改配置单机配置集群配置分库分表配置1.修改全局配置2.实例配置垂直分库水平分库3.修改group-instance.xml4.启动监听四、配置canal-adapter1修改启动配置2配置映射文件3启动ES数据同步查询所有订阅同步数据同步开关启动4.验证五、配置canal-admin一、概述简介canal是Alibaba旗下的一款开源项目,Java开发。基于数据库增量日志解析,提供增量数据订阅&消费。Git地址:https://github.co