一、关联式容器特性
set和map都是(树形)关联式容器,和前面STL中的vector、list、deque等容器类(序列式容器)不同,前者关联式容器中存储的是pair<key,value>结构的键值对(其中key代表键值,value代表与key对应的信息)(key对应的其它信息,例如key表示学号,value可以表示姓名),后者容器中只存储元素本身,因此前者数据检索的效率比序列式子容器更高。
二、set
1、定义
set是按照一定次序存储元素的容器(底层是二叉搜索树)。set通过key的值来实现搜索查找的,并且常常不用描述value的信息,是因为set底层实际存放的是由<key,key>构成的键值对,因此set再插入元素时间,只需插入key就行,不需要去构造键值对,另外key为const类型,因此不能修改key的值,不然会破坏二叉搜索树的结构。下面本文将介绍set的特性以及常见的功能。
2、插入数据
set容器中的插入函数是insert,其返回值是pair<iterator,bool>(返回值有两个,用pair类包装起来),前面数据表示插入数据的迭代器,后面表示数据是否插入成功,当插入set容器中已经存在的元素,后面的bool值就会为零,同时返回的迭代器为与这个key重复位置的迭代器,因为set具有去重复性。
int main() {set<int> st;//set中只存放key,单纯的key搜索树,其参数为const只读性pair<set<int>::iterator,bool> pairs = st.insert(4); //返回值有两个为pair<first,second>类类型cout << *(pairs.first) << endl;//pairs.first 当前插入数据的迭代器cout << pairs.second << endl;//pairs.second,插入是否成功st.insert(1);st.insert(15);st.insert(6);st.insert(9);cout << st.size() << endl;st.insert(6);st.insert(9);st.insert(15);cout << st.size() << endl;return 0;}
输出结果:
3、迭代器
set也具有迭代器功能,通过中序遍历的顺序(二叉搜索树通过中序遍历实现存储数据的排序功能,并且时间复杂度为logN)来重载迭代器++,进而实现迭代器的功能。
int main() {set<int> st;st.insert(1);st.insert(15);st.insert(6);st.insert(9); st.insert(4);set<int>::iterator it = st.begin();while (it !=st.end()) {cout << *(it++) << " ";}cout << endl;for (auto& ret : st) {cout << ret << " ";}cout << endl;}
输出结果:
4、数据查找
set容器中数据查找常用的有两个函数,一个是find,另一个是count,前者返回值是查找元素的迭代器,没找到返回end()迭代器,后面返回值类型是size_t,但是要么是1表示元素找到,要么是0表示元素没有找到。
int main() {set<int> st;st.insert(1);st.insert(15);st.insert(6);st.insert(9); st.insert(4); set<int>::iterator its = st.find(6);//没找到返回end()迭代器,找到了,返回当前位置迭代器 while (its != st.end()) {cout << *(its++) << " "; } cout << endl; size_t count = st.count(4);//返回元素的个数,由于set具有去重的效果,因此 //这里的count函数返回 值只有1和0,比find查找效果更直观 cout << count << endl;}
输出结果:
5、元素删除
set中删除函数有三种方式:
a.直接传key值删除,容器中没有存储这个key元素,则对容器不做任何处理。
b.迭代器删除,先找到对应位置的迭代器,然后删除迭代器位置对应的元素,当元素迭代器没有找到的时候,继续执行删除指令,程序会崩溃。
c.范围删除通过函数lower_bound(val)和upper_bound(val),来查找要删除的迭代器范围(也可以直接找到指定元素的迭代器如同方法b,然后在执行范围删除),其中lower_bound(val)和upper_bound(val)分别返回大于等于val的第一个迭代器和返回比val大的第一个迭代器,删除范围为[itlow,itup),如果itlow == itup,则不删。
d.使用equal_range(val)函数查找对应迭代器的位置,这个函数有两个返回值,一个是lower_bound(val)的迭代器,另一个是upper_bound(val)迭代器,删除方法和方法c类似。
int main(){size_t count = st.erase(6);//erase返回值为删除元素个数,直接删除cout << count << endl;for (auto& ret : st) {cout << ret << " ";}cout << endl;set<int>::iterator its = st.find(1);st.erase(its);//没有返回值(迭代器删除)set<int>::iterator itlow = st.lower_bound(5);//返回大于等于5的第一个迭代器(9)set<int>::iterator itup = st.upper_bound(5);//返回比5大的第一个迭代器(9)st.erase(itlow, itup);//范围删除,删除范围[itlow,itup),如果itlow == itup,不删for (auto& ret : st) {cout << ret << " ";}cout << endl;pair<set<int>::iterator, set<int>::iterator> ret = st.equal_range(9);st.erase(ret.first, ret.second);for (auto& ret : st) {cout << ret << " ";}cout << endl;return 0;}
输出结果:
二、multiset容器
1、定义
和set容器功能基本相同,但是其底层是变异搜索树,可以存入相同元素,因此该容器不具有去重的功能。
int main() {multiset<int> mst;mst.insert(4);mst.insert(1);mst.insert(15);mst.insert(6);mst.insert(9);cout << mst.size() << endl;mst.insert(6);mst.insert(9);mst.insert(15);cout << mst.size() << endl;for (auto& ret : mst) {cout << ret << " ";}cout << endl;return 0;}
输出结果:
2、count和erase函数
增查功能和set几乎一样,这里不再陈述,只不过multiset容器使用迭代器查找功能返回的是相同元素的第一个的迭代器的位置。但是count和直接删除(不使用迭代器)时,返回值是对应找到元素的个数或者删除元素的个数(全删)。
int main() {multiset<int> mst;mst.insert(4);mst.insert(1);mst.insert(15);mst.insert(6);mst.insert(9);mst.insert(6);mst.insert(9);mst.insert(15);cout << endl;size_t count1 = mst.count(4);cout << "找到元素的个数:"<< count1 << endl;size_t count2 = mst.erase(6);//删除全部6cout <<"删除元素的个数:" << count2 << endl;return 0;}
输出结果:
四、map
1、定义
map也是关联容器,它通过key的值(唯一的排序标识符)来进行排序,存储键值对为pair<key,value>,key和value类型可以不相同。在map容器内部,key和value通过成员类型value_type绑定在一起,并称为pair和上面介绍的pair一样。另外map不仅有set具有的功能,而且还能支持下标访问,即在[ ]中放入key,就能找到对应value。
2、插入元素
由于map类中的键值对存放在pair类中,要插入元素时,首先要对键值对进行构造,调用pair构造函数(一般用匿名构造比较方便),然后再进行插入。除此之外还有一种方法就是先调用make_pair()函数的构造,然后再进行,插入,因为make_pair()是一个类模板修饰的函数,能够根据参数的形式来推导其类型(但是一般情况下,map模板实例化的时候就已经决定了存储参数的类型,即key和value的类型已经被决定了)。
int main() {map<int, string> mp;//当map中的键值对确定之后,后面插入的类型都当作该键值对类型mp.insert(pair<int, string>(8,"菠萝"));//mp容器中存储的是键值对pair类型mp.insert(pair<int, string>(5, "梨"));mp.insert(make_pair(6, "桃"));mp.insert(make_pair(3, "橘子"));mp.insert(make_pair(10, "橙子"));//由于键值对类型已经确定了<int,string>cout << mp.size() << endl;mp.insert(make_pair(10, "橙子"));//和set一样具有去重性。cout << mp.size() << endl;map<int, string>::iterator it = mp.find(3);it.operator*().second.operator+=("苹果");//可以对string类型操作for (pair<int,string> e : mp) {cout << e.first<<" " << e.second << endl;}return 0;}
输出结果:
3、元素删除
元素删除和查找功能和set一样,只是访问的时候要用当作键值对去访问其中的内容。
int main() {map<int, string> mp;//当map中的键值对确定之后,后面插入的类型都当作该键值对类型mp.insert(pair<int, string>(8,"菠萝"));//mp容器中存储的是键值对pair类型mp.insert(pair<int, string>(5, "梨"));mp.insert(make_pair(6, "桃"));mp.insert(make_pair(3, "橘子"));mp.insert(make_pair(10, "橙子"));//由于键值对类型已经确定了<int,string>size_t count = mp.erase(6);//erase返回值为删除元素个数,直接删除cout << "是否删除成功:" << count << endl;cout << "第一次删除后剩下的元素:" << endl;for (auto& ret : mp) {cout << ret.first << " "<<ret.second;cout << endl;}map<int, string>::iterator its = mp.find(3);mp.erase(its);//没有返回值(迭代器删除)cout << "第二次删除后剩下的元素:" << endl;for (auto& ret : mp) {cout << ret.first << " " << ret.second;cout << endl;}map<int, string>::iterator itlow = mp.lower_bound(5);//返回大于等于5的第一个迭代器(8)map<int, string>::iterator itup = mp.upper_bound(5);//返回比5大的第一个迭代器(8)mp.erase(itlow, itup);//范围删除,删除范围[itlow,itup),如果itlow == itup,不删cout << "第三次删除后剩下的元素:" << endl;for (auto& ret : mp) {cout << ret.first << " " << ret.second;cout << endl;}pair<map<int, string>::iterator, map<int, string>::iterator> ret = mp.equal_range(8);mp.erase(ret.first, ret.second);cout << "第二次删除后剩下的元素:" << endl;for (auto& ret : mp) {cout << ret.first << " " << ret.second;cout << endl;}return 0;}
输出结果:
4、map下标访问
map和set有个很大的不同点就是类容器中多了一个下标访问,也就是对操作符[ ]的重载。
从上面的对[ ]操作符重载的函数来看,可以发现其中其主要作用的就是insert()函数,插入类型为键值对(mapped_type会自动调用默认构造函数,不管是内置类型还是自定义类型),其返回值也是一个键值对pair<iterator,bool>,().first,访问的是迭代器,*(().first),访问的是迭代器中的内容,因为迭代器中存储的map数据也是键值对,此时的().second访问的是键值对中的信息内容即key对应的value。通过上面的分析,可得出结论:
[ ]的含义就是在map容器中插入一个新的键值对,如果新的键值对在容器中已经存在,那么insert就会插入失败,bool返回值为0,迭代器返回值是map容器中和这个元素重复的迭代器,因此().second访问的是重复这个元素的value;如果插入成功,则().second访问的是新插入键值对的value信息,从而可以对其进行操作。
int main() {map<int, string> mp1;mp1[8];//插入键值,value会自动调用默认构造赋值为空mp1[8] = "菠萝";cout << mp1[8] << endl;//输出key对应的value信息for (auto& ret : mp1) {cout << ret.first << " " << ret.second;cout << endl;}mp1[8] = "苹果";//根据键值修改value信息for (auto& ret : mp1) {cout << ret.first << " " << ret.second;cout << endl;}return 0;}
输出结果:
五、muitimap
multimap和map的区别同上面set和multiset区别一样,只是多了一个不能通过[ ]修改信息,因为multimap中存在多个key值,会造成访问混乱,进而导致修改失败。。