一、set(multiset)的基本知识和使用
set也是一种我们直接可以使用的容器,使用应该包含 #include <set> 这个头文件。此处暂且不讨论其底层,只探讨set如何使用即可。
我们看到,set 的模板参数有三个,第一个就是其存储的数据类型,第二个是仿函数,决定到时候排序的逻辑,第三个是空间配置器,我们在使用中着重写第一个,而后两个我们目前的学习程度几乎用不到。
1. set的构造
我们可以看到,set的构造基本上和我们学的vector,list等容器的构造类似,因为set也是支持迭代器的,所以也是很简单的。
2. set的增删查
我们来看看下面的代码:
int main()
{set<int> s1;//set的插入s1.insert(4);s1.insert(1);s1.insert(9);s1.insert(5);for (auto a : s1){cout << a << " ";}cout << endl;auto found1 = s1.find(10);//找不到即返回end()if (found1 == s1.end())cout << "找不到" << endl;auto found2 = s1.find(5);s1.erase(found2);//支持迭代器删除s1.erase(4);//支持直接删除内容//注意,删除同样会导致迭代器失效for (auto a : s1){cout << a << " ";}cout << endl;return 0;
}
运行结果:
set在插入过程中,底层已经按照升序,将插入的内容排号顺序了,我们在删除的过程中,set仍然保持着升序。需要注意的是,当我们使用find功能时,如果没有查找到,则会返回set的end()迭代器。同样的,erase也支持迭代器区间删除,如果执行 erase(s1.begin(),s1.end()); 那么我们将删除set中所有的元素。
multiset和set的使用基本完全类似,因为set是不能接受相同的值的,也就是说我们如果插入了一个set中已经存在的值,那么我们会插入失败,而multiset不会插入失败,因为multiset支持值冗余。结果就是二者均会默认升序,而set会去重,multiset不会。
再是multiset中的find和erase功能,其中由于multiset会存在多个值,因此find返回的是其 中序第一个值得迭代器。而erase删除时,如果该值存在多个,erase会将其全部删除。
二、map的基本知识和使用
这是map的声明,Key就是map底层关键字的类型,而T是map底层value的类型,同理set,一般情况下我们不需要传入后两个模板参数,只需要传入Key 和 T 即可。
1.pair类型
map的底层数据存储,是用pair<Key,T>来存储键值对数据。
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){}template<class U, class V>pair(const pair<U, V>& pr) : first(pr.first), second(pr.second){}
};
这就是pair的简单实现,我们从上面就可以看出来,pair内可以存储两个分别为T1 和 T2 类型的数据,而map的插入或者构造是需要pair的。
2. map的构造
还是一样,构造和前面没什么太大的区别,我们直接来看map的增删查改。
3.map的增删查
int main()
{map<string, string> m1;pair<string, string> p1("地图", "map");m1.insert(p1);m1.insert(pair<string, string>("你好", "hello"));m1.insert({ "自动的","auto" });for (auto a : m1){cout << a.first << "->" << a.second << endl;}return 0;
}
运行结果:
map在插入过程中,会按照Key的大小进行自动排序,因此map一定程度上也可以帮助我们实现排序有关功能。
m1.erase("你好");
auto found = m1.find("地图");
cout << found->first << "->" << found->second << endl;
cout << endl;
for (auto a : m1)
{cout << a.first << "->" << a.second << endl;
}
运行结果如下:
也就是说,map的增删查和set基本一致,要注意的是map的插入是一个pair的类型,而删除和查找,只对map的Key有效,我们不能使用map的value来进行删除和查找。
4. map的[]功能样例
我们来看下面的代码:
int main()
{map<string, int> countMap;string arr[] = { "苹果","香蕉", "凤梨", "苹果", "猕猴桃", "香蕉", "苹果" };for (const auto& str : arr){countMap[str]++;}for (const auto& e : countMap){cout << e.first << ":" << e.second << endl;}//修改凤梨的个数countMap["凤梨"] = 20;cout << endl;for (const auto& e : countMap){cout << e.first << ":" << e.second << endl;}return 0;
}
运行结果如下:
这是我们一个统计水果个数的简单代码,通过map来实现,我们在下面也通过了[]来实现了修改map中Key值对应的value的数据,那么map中的[]该如何理解呢?
其实本质很简单,[]是运算符重载,如果map中没有[]中的Key值,例如上方在[]中写的str,那么我们就会插入该Key值,对应的value值是value所对类型的默认值,然后我们可以对该Key所对的value进行修改例如上方的++。如果Key已经存在,那么我们就只能对齐value进行修改,例如上方对凤梨个数的修改。
此时map中没有西瓜这个水果,也就是没有西瓜这个Key,那么我们只进行countMap["西瓜"];操作,会发生什么呢?完全正确,map会插入西瓜作为Key而其对应的value类型为int,其默认值为0,所以到时我们打印出来的就是 西瓜:0。
以上内容如有错误,欢迎批评指正!