目录
set
什么是set
C++中的set是一种基于红黑树实现的关联容器,用于存储唯一且已排序的元素集合。set中的元素按照一定的顺序排列,插入和删除操作的时间复杂度为O(log n),查找操作的时间复杂度也为O(log n)。set中的元素必须是可比较的,因此需要重载运算符或者提供自定义比较函数。
如图set分为普通set以及multiset,普通set就是上面所说存储唯一且已排序的元素结合,而multiset就是可以存储多个相同key的set
那么我们先开始进行set的学习
set的使用
跟以往学过的STL容器一致,set的使用如出一辙,所以相同的函数接口就不做过多的赘述,主要讲一下用法上的区别,和没见过的几个接口函数的使用!
insert
首先,测试一下insert函数接口
void test_set1()
{
// 查找在不在
// 排序 + 去重
set<int> s;
s.insert(5);
s.insert(2);
s.insert(6);
s.insert(1);
s.insert(1);
s.insert(1);
s.insert(2);
set<int>::iterator it = s.begin();
while (it != s.end()) {
// *it = 10;
cout << *it << " ";
++it;
}
cout << endl;
}
如图最终通过迭代器打印发现实现了1的去重,以及排序!
所以在一个场景中,如果一段数据需要去重以及排序,我们可以通过将这段数据借助set输入,然后实现去重和排序
另外set的key值不可修改! 体现在解引用时不能赋值
erase
在insert部分的代码上加入即可测试erase
// 删除存在的
s.erase(2);
// 删除不存在的
s.erase(3);
for (auto e : s) {
cout << e << " ";
}
cout << endl;
不会导致报错,表示删除set中不存在的元素,不会产生异常
另外我们在阅读erase这个函数接口的文档时 ,观察到它支持迭代器区间删除,那么我们就可借助find来定位,然后通过迭代器删除
void test_set1()
{
set<int> s;
s.insert(7);
s.insert(9);
s.insert(6);
s.insert(5);
s.insert(3);
s.insert(1);
set<int>::iterator it = s.begin();
while (it != s.end()) {
// *it = 10;
cout << *it << " ";
++it;
}
cout << endl;
set<int>::iterator it1 = s.find(3);
auto it2 = s.find(7);
s.erase(it1, it2);
for (auto e : s) {
cout << e << " ";
}
cout << endl;
}
由此可得删除的区间符合左闭右开原则
lower_bound和upper_bound
set中的lower_bound和upper_bound是两个函数,它们都用于在有序集合中查找元素。其中,lower_bound函数返回一个迭代器,指向第一个不小于给定值的元素,而upper_bound函数返回一个迭代器,指向第一个大于给定值的元素。如果给定值在集合中不存在,那么lower_bound和upper_bound函数都返回一个指向第一个大于给定值的元素的迭代器.
void test_set3()
{
std::set<int> myset;
std::set<int>::iterator itLow, itUp;
for (int i = 1; i < 10; i++) myset.insert(i * 10);
// 10 20 30 40 50 60 70 80 90
//itlow = myset.lower_bound(30); //
itLow = myset.lower_bound(25); // >= val值位置的iterator
itUp = myset.upper_bound(70); // > val值位置的iterator
// [25, 70]
myset.erase(itLow, itUp); // 10 20 70 80 90
for (auto e : myset)
{
cout << e << " ";
}
cout << endl;
}
对于lower和upper的使用中,itLow取到了30所在位置的迭代器地址,itUp取到了80位置的迭代器,再通过左闭右开原则,删除时删掉了30 40 50 60 70,对于范围删除我们也可以借助upper和lower来确定迭代器大致范围,用find的话就需要知道这个数据是否存在于set中。
count
set中的count()函数用于统计某个元素在set容器中出现的次数。如果该元素存在,则返回1,否则返回0。这个对于set来说有点鸡肋,count主要是在multiset发挥作用
equal_range
在cpp文档中我们知道了它的返回值是pair,也就是键值对。并且内部存放了lower_bound和upper_bound
(pair是C++ STL中的一个模板类,用于将两个不同类型的值组合在一起。其中,第一个值的类型被称为first,第二个值的类型被称为second)
// 测试equal_range
void test_set8()
{
set<int> myset;
set<int>::iterator itLow, itUp;
for (int i = 1; i < 10; i++) myset.insert(i * 10);
// 10 20 30 40 50 60 70 80 90
pair<set<int>::iterator, set<int>::iterator> p1 = myset.equal_range(30);
cout << "the lower bound points to: " << *p1.first << endl;
cout << "the upper bound points to: " << *p1.second << endl;
pair<set<int>::iterator, set<int>::iterator> p2 = myset.equal_range(25);
cout << "the lower bound points to: " << *p2.first << endl;
cout << "the upper bound points to: " << *p2.second << endl;
}
到了这里set的初步使用的讲解也就结束了,接下来我们讲一下multiset吧
multiset的使用
实现相同数据的插入
void test_set5()
{
multiset<int> ms;
ms.insert(5);
ms.insert(2);
ms.insert(6);
ms.insert(1);
ms.insert(1);
ms.insert(1);
ms.insert(2);
set<int>::iterator it = ms.begin();
while (it != ms.end()) {
// *it = 10;
cout << *it << " ";
++it;
}
cout << endl;
}
删除掉符合的所有数据
在insert的基础上进行删除1的key
// 记录删除个数
size_t n = ms.erase(1);
cout << "n: " << n << endl;
for (auto e : ms)
{
cout << e << " ";
}
cout << endl;
我们可以用n来记录erase的数据个数
查找并返回第一个符合的数据
void test_set6()
{
multiset<int> ms;
ms.insert(5);
ms.insert(2);
ms.insert(6);
ms.insert(1);
ms.insert(1);
ms.insert(1);
ms.insert(2);
set<int>::iterator it = ms.find(2);
while (it != ms.end()) {
// *it = 10;
cout << *it << " ";
++it;
}
cout << endl;
}
计数某个数据个数
// 测试multiset下的count
void test_set7() {
multiset<int> ms = { 1,1, 2, 3, 3, 3, 3, 4, 5 };
cout << ms.count(1) << endl; // 输出:2
cout << ms.count(3) << endl; // 输出:4
cout << ms.count(6) << endl; // 输出:0
}
显而易见,count在multiset中有特别大的作用,我们可以将文本插入multise<string>然后读取,某一个单词,来记录出现的次数
取出相同数据的前后迭代器
// 测试multiset下的equal_range
void test_set9() {
multiset<int> ms;
multiset<int>::iterator itLow, itUp;
for (int i = 1; i < 7; i++) ms.insert(i * 10);
ms.insert(30);
ms.insert(30);
ms.insert(30);
for (auto e : ms)
cout << e << " ";
cout << endl;
pair<multiset<int>::iterator, multiset<int>::iterator> p1 = ms.equal_range(30);
itLow = p1.first;
itUp = p1.second;
while (itLow != itUp) {
cout << *itLow << " ";
++itLow;
}
cout << endl;
}
map
在map中我们就主要讲一下普通的map,multimap仅仅也是支持相同名称的key,并没有什么特殊的用法!不过multimap不支持 [] 重载
map的插入与遍历
// map的插入和遍历
void test_map1()
{
map<string, string> dict;
// 匿名对象构造pair对象
dict.insert(pair<string, string>("语文", "Chinese"));
dict.insert(pair<const char*, const char*>("数学", "Math"));
// 通过函数模版 make_pair 自适应类型
dict.insert(make_pair("英语", "English"));
map<string, string>::iterator it = dict.begin();
while (it != dict.end())
{
// 迭代的it下存放的是 pair对象
cout << (*it).first << ":" << (*it).second << endl;
++it;
}
for (auto& e : dict)
{
// e.first = 'x'; first不可修改
// e.second = 'x'; second支持修改
cout << e.first << ":" << e.second << endl;
}
}
在说map的insert()之前,我们需要了解pair和pair与insert的关系
map的[]重载
[]重载的原理
// 测试map中的[]
void test_map2() {
string str[] = { "嗯","哦","噢","喔","好","噢","噢","好" };
map<string, int> countMap;
for (auto& s : str) {
// 常规实现计数要求
//map<string, int>::iterator ret = countMap.find(s);
//
// 找不到
//if (ret == countMap.end())
// countMap.insert(make_pair(s, 1));
//else
// (*ret).second++;
// map中实现计数要求
// 借助[]重载实现
countMap[s]++;
// 分成多样的功能
}
for (auto& s : countMap) {
cout << s.first << ":" << s.second << endl;
}
}
我们在搜索二叉树的学习中,实现过类似一段话的单词出现个数的记录,核心代码
但是map中重载了[]来便于实现这一块的内容,以及更深层次的东西,现在我们开始研究一下map中的operator[]
从这个图我们知道map的[]重载设计较复杂。
1.关于insert的返回值pair<iterator, bool>以及传入值pair<key, value_type>类型,虽然都是pair但是pair内存储变量类型不同
2.借助insert返回的这个pair<iterator, bool>的first找到迭代器位置,在通过迭代器解引用来找到存放的map<string, int>的某一个节点存放的pair<first, second>,最终通过这个pair对second进行操作。其中first为key,second为value,对应下图为好 与 2
讲到这里[]重载的原理就差不多了
[]体现的功能
// 测试[]的功能
void test_map3() {
map<string, string> dict;
dict["语文"]; // 插入
dict["语文"] = "Chinese"; // 修改
cout << dict["语文"] << endl; // 访问
dict["数学"] = "Math"; // 插入 + 修改
for (auto& s : dict) {
cout << s.first << ":" << s.second << endl;
}
}
这一部分就看代码注释就好!
map与multimap
几乎一致只是multimap不支持[]重载,这个我们也能理解,因为multimap可以实现多个相同的key,那么就会导致[]查找的second,不知道查找哪一个!
multimap应用场景
// 测试multimap
void test_map4() {
multimap<string, string> ms;
ms.insert(make_pair("Chinese", "中文"));
ms.insert(make_pair("Chinese", "中国人"));
for (auto& s : ms) {
cout << s.first << " " << s.second << endl;
}
}
来实现字典的一词多意