前言:
我们前面一起学习了二叉搜索树,这便是为了引入本章我们所学的map和set容器。map和set的底层实现就和二叉搜索树有关...
目录
(一)键值对的引入
(1)关联式容器
(2)键值对
(二)set
(1)set的介绍
(2)set的使用
set的插入:
set的查找:
set的删除:
(3)multiset的介绍+使用
(三)map
(1)map的介绍
(2)map的使用
map的插入:
std::map::operator[]的使用(*重点):
关联式容器也是用来存储数据的,与序列式容器不同的是,其里面存储的是结构的 键值对,在数据检索时比序列式容器效率更高。
通常我们学习的都是树状结构的关联式容器
总结:
序列式容器:
树状结构的关联式容器:
关联式容器存放的是键值对,那么键值对是什么呢?
用来表示具有一一对应关系的一种结构,该结构中一般只包含两个成员变量key和value,key代表键值,value表示与key对应的信息。
它实际上就是一个类模板,用来表示具有一 一对应关系的一种结构。
STL源码库给出的源代码如下:
使用 set 容器,必须引入该头文件#include < set >
set的使用文档
介绍:
和很多容器一样,set设置了插入的接口,但是set的插入是有额外的功能性的!
我们试用下面的代码:
void test_set1()
{
set s1;
s1.insert(2);
s1.insert(6);
s1.insert(2);
s1.insert(3);
s1.insert(3);
s1.insert(5);
s1.insert(8);
s1.insert(8);
s1.insert(6);
s1.insert(7);
//两种迭代打印的方法::
set::iterator it = s1.begin();
while (it != s1.end())
{
// 搜索树不允许修改key,可能会破坏搜索的规则
//*it1 += 1;
cout << *it << " ";
++it;
}
cout << endl;
for (auto e : s1)
{
cout << e << " ";
}
}
输出结果:
set实现了去重和排序:
同样的迭代器的使用方式也一样,只是这里的const迭代器和普通迭代器都是一样的,都不支持修改,原因是如果对二叉搜索树进行修改的话,很有可能会导致整棵树的结构被打乱,所以不支持修改。
应用代码:
void test_set2()
{
// 排序 + 去重
set s1;
s1.insert(2);
s1.insert(6);
s1.insert(2);
s1.insert(3);
s1.insert(3);
s1.insert(5);
s1.insert(8);
s1.insert(8);
s1.insert(6);
s1.insert(7);
int x;
while (cin >> x)
{
/*auto ret = s1.find(x);
if (ret != s1.end())
cout << "在" << endl;
else
cout << "不在" << endl;*/
if (s1.count(x))
cout << "在" << endl;
else
cout << "不在" << endl;
}
}
这里find的用法和算法库里的一样,但是为什么要再在set中创立一个呢?
set自带的查找和算法库中的查找有什么区别
set s;
s.insert(4);
s.insert(5);
s.insert(2);
s.insert(1);
s.insert(1);
s.insert(3);
s.insert(2);
s.insert(1);
s.erase(3);//直接给值删除
for (auto e : s)
{
cout << e << " ";
}
cout << endl;
int x;
while (cin >> x)
{
set::iterator pos = s.find(x);
if (pos != s.end())
{
s.erase(pos);//迭代器删除
cout << "删除" << x << "成功" << endl;
}
else
{
cout << x << "不在set中" << endl;
}
for (auto e : s)
{
cout << e << " ";
}
cout << endl;
}
删除接口也没什么可以过多介绍的了,还有size、empty、clear等接口和我们之前学的容器接口用法一样,大家可以自己实践一下!
set会自动实现去重的操作,那我们只想拥有排序等相关的操作,而不想去重,这里STL也为我们提供了multiset容器。
介绍:
用法:
void test_set3()
{
multiset s1;
s1.insert(1);
s1.insert(1);
s1.insert(2);
s1.insert(3);
s1.insert(3);
s1.insert(4);
s1.insert(4);
s1.insert(4);
s1.insert(5);
multiset::iterator it = s1.begin();
while (it != s1.end())
{
cout << *it << " ";
it++;
}
cout << endl;
auto ret = s1.find(1);
cout << *ret << endl;
while (ret != s1.end() && *ret == 1)
{
cout << *ret << " ";
++ret;
}
cout << endl;
cout << s1.count(1) << " ";
cout << s1.count(5) << " ";
}
输出结果:
这里我们看到,可以打印出重复的数据了,此时count的作用可以计数了,不需要局限于和find同等的用处。
使用 map 容器,必须引入该头文件#include < map >
map的使用文档
其实map的操作也和其他容器的操作是一样的,但由于map中存放的是键值对,所以我们以插入为例来介绍如何使用:
void test_map1()
{
map dict;
//匿名对象的好处
dict.insert(pair("sort", "排序"));
//不用匿名对象
pair kv("insert", "插入");
dict.insert(kv);
//make_pair是函数模板
dict.insert(make_pair("left", "左边"));
//可以这么写但是别这么用(隐式类型的转换) -- C++11再讲
dict.insert({ "right", "右边" });
//map的遍历
map::iterator it = dict.begin();
while (it != dict.end())
{
//cout << *it << endl;//it->operator*() -- C++不支持返回两个值
//cout << (*it).first << ":" << (*it).second << endl;
cout << it->first << ":" << it->second << endl;
it++;
}
cout << endl;
for (const auto& kv : dict)
{
cout << kv.first << ":" << kv.second << endl;
}
}
我们这里使用匿名对象配合insert使用非常方便,库中也给了make_pair函数模板来表示键值对的创建。
因为C++不支持返回两个值,所以我们这里用到了pair,通过pair的first和second,即可访问到两个值。
同时,map迭代器的使用方法和其他容器迭代器的使用方法一样,这是STL设计时为了方便使用,所采用的高维度泛型设计。
Key_value模型中,修改不能修改key,但是可以修改value。
引入:
我们利用map来统计各个水果出现的次数:
通过查找来挨个遍历查找统计个数
void test_map2()
{
string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果",
"西瓜", "苹果", "香蕉", "苹果", "香蕉" };
map countMap;
for (auto& str : arr)
{
map::iterator it = countMap.find(str);
if (it != countMap.end())
{
it->second++;
}
else
{
countMap.insert(make_pair(str, 1));
}
}
}
注意:
insert的返回值是一个pair,pair的first是一个迭代器(如果插入成功了指向新插入的位置,如果插入失败了,则返回已经存在的结点的位置),pair的second的一个bool值,插入成功是返回true,失败是返回false。
所以我们还可以通过返回值来统计个数
但是map库中给出 operator[ ] ,其实他的应用会让上述郭晨改变的更简单易懂!
其中可以对文档中的最后一句对此函数的调用等效于:
我么能深入探讨一下,理解如下:
所以operator[ ]的参数是key,返回值是value的引用!!!
我们可以用operator[ ]来统计次数:
因为返回的是value的引用所以,可以修改,这样一来,operator[]兼顾了两个功能:插入 + 修改
同样当map需要插入的时候,也就有了如下的写法:
void test_map2()
{
map dict;
//dict.insert(pair("sort", "排序"));
dict.insert(make_pair("sort", "排序"));
dict.insert(make_pair("string", "字符串"));
dict.insert(make_pair("count", "计数"));
dict.insert(make_pair("count", "(计数)"));//插入失败
dict["left"] = "左面";//插入+修改
dict["right"];//插入
dict["count"] = "(计数)";//修改
cout << dict["count"] << endl; // 查找
//map::iterator dit = dict.begin();
auto dit = dict.begin();
while (dit != dict.end())
{
//cout << (*dit).first << ":" << (*dit).second << endl;
cout << dit->first << ":" << dit->second << endl;
++dit;
}
cout << endl;
}
operator[ ]的使用不仅可以简化计数等,还可以兼顾插入、修改、查找等作用!大家重点理解!
感谢您的阅读!