map和set的使用指南
1.关联式容器和序列式容器
在我们之前的学习当中,vector,list,deque,forward_list为序列式容器,(stack,queue为容器适配器,这里顺带着大家复习一下,如果忘记了可以看前面的篇章),而我们今天学习的map和set为关联式容器。
其两者的根本区别是序列式容器存储的是数值本身,而我们的关联式容器存储的是**<key,value>类型的键值对**
键值对的介绍:用来表示具有一一对应关系的一种结构,该结构中一般只包含两个成员变量key和value,key代表键值,value表示与key对应的信息。
其实我们今天要讲的map和set其底层是一种以树形结构的关联式容器——红黑树,这个在之后的文章会为大家一一讲解。
2.set的使用
2.1 set的模板参数列表
T: set中存放元素的类型,实际在底层存储<value, value>的键值对。
Compare:set中元素默认按照小于来比较
Alloc:set中元素空间的管理方式,使用STL提供的空间配置器管理
2.2set的常见使用案例插入
int main()
{
set<int> a;
multiset<int> b;
a.insert(2);
a.insert(2);
a.insert(1);
a.insert(3);
b.insert(2);
b.insert(2);
b.insert(1);
b.insert(3);
set<int>::iterator pos1 = a.begin();
while (pos1 != a.end())
{
cout << *pos1 << "";
pos1++;
}
cout << endl;
multiset<int>::iterator pos2 = b.begin();
while (pos2 != b.end())
{
cout << *pos2 << "";
pos2++;
}
}
这里设计到了multiset,其作用就是不去重。
上段代码以及编译结果可以看出,set的插入会自动去重,而且会排序(底层用的快排,当然在char类的排序中运用的适合strcpy一样的原理,在底层树形结构中用的也是中序遍历),而我们的multiset除了不去重之外,其余接口都和set的使用方式一样。
2.3此类问题一些迭代器的用法以及感悟
首先map也好set也罢其底层用的容器是列表,具之前文章所刨析,列表迭代器需要重新封装,所以最后其用法与string,vector等无异(都认为其类型为一个指针变量),只是不能遍历。
当然在此有个需要注意的地方,list迭代器的将“—>"重载时,其返回的是当前变量的地址。
2.4 set的删除
int main()
{
set<int> a;
multiset<int> b;
a.insert(2);
a.insert(2);
a.insert(1);
a.insert(3);
set<int>::iterator pos = a.begin();
set<int>::iterator pos2 = a.find(3);
int re = a.erase(2);
a.erase(pos2);
cout << re << endl;
while (pos != a.end())
{
cout << *pos;
pos++;
}
cout << endl;
}
首先这里用了删数字和地址的方式删除了2,3,其次其erase函数返回规则是删除几个数返回几,如果没删除返回0。
tips:在multiset当中使用find的话如果恰巧找的那个数是重复的,那么将返回中序第一个val值所在结点的迭代器。
*pos无非被修改,因为它是一个数的结构,贸然修改其结构不保。
3.map的使用
3.1map的模板参数说明
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-E9Hww6qX-1679380899185)(C:\Users\山野村居\Desktop\4.png)]
key: 键值对中key的类型
T: 键值对中value的类型
Compare: 比较器的类型,map中的元素是按照key来比较的,缺省情况下按照小于来比
较,一般情况下(内置类型元素)该参数不需要传递,如果无法比较时(自定义类型),需要用户
自己显式传递比较规则(一般情况下按照函数指针或者仿函数来传递)
Alloc:通过空间配置器来申请底层空间,不需要用户传递,除非用户不想使用标准库提供的空间配置器
tips:当key不存在时,operator[]用默认value与key构造键值对然后插入,返回该默认value,at()函数直接抛异常。
3.2map插入
int main()
{
map<string, string> a;
pair<string, string> b("telephone","电话");
a.insert(b);
a.insert(pair<string, string>("cake", "蛋糕"));//需要写明白类型
a.insert(make_pair("love", "爱"));//自动推导类型,函数返回的是pair
map<string, string>::iterator it = a.begin();
while (it != a.end())
{
cout << (*it).first << endl;// "."的优先级比*高所以加(),first指向的是key,second指向的是value,当然了我们正常是直接使用->符号,但是这样编译器会省略一个->,因为容器list对->的重载返回的是地址
it++;
}
}
这里的插入比起set来说细节更多,请一定要多多注意理解。
3.3对于for快捷遍历的一些思考
3.4map对[ ]运算符的重载
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UpbHvXVE-1679380899187)(C:\Users\山野村居\AppData\Roaming\Typora\typora-user-images\image-20230321141803007.png)]
从这张图片可以看出其返回的是mapped_type类型的引用,而我们的里面实际上是pair这个对象里面的second。
相当于key,value这个模型中给个key能访问到其value。(tips:multimap 里面没有重载multimap,此外在multimap中还有一个常用的cout计数函数)
使用案例演示:
int main
{
map<string,string> a;
a.insert(make_pair("a","小明"));
a.insert(make_pair("b","小红"));
a.insert(make_pair("c","小张"));
a["a"] = "小李"; //修改
a["d"]; //当没有这个key时自动插入
cout<<a["c"]; //打印结果为小张
return 0;
}