快捷搜索:  汽车  科技

stl容器有哪几种,Cpp浅析系列-STL之set

stl容器有哪几种,Cpp浅析系列-STL之setbegin()返回指向第一个元素的迭代器clear()清除所有元素count()返回某个值元素的个数,0或1,可以用来判断是否存在某数empty()如果集合为空,返回trueend()返回指向最后一个元素的迭代器equal_range()返回集合中与给定值相等的上下限的两个迭代器erase()删除集合中的元素set集合中的删除操作是不进行任何的错误检查的,比如定位器的是否合法等等,所以用的时候自己一定要注意。find()返回一个指向被查找到元素的迭代器;如果没有找到,返回指向集合最后一个元素的迭代器。get_allocator()返回集合的分配器insert()在集合中插入元素可以在集合中插入其他数组中指定个数的值lower_bound()返回指向大于(或等于)某值的第一个元素的迭代器key_comp()返回一个用于元素间值比较的函数max_size()返回集合能容纳的元素的最大限值rbe

前言

stl容器有哪几种,Cpp浅析系列-STL之set(1)

CPP

集合(Set)t是一种元素唯一的、包含已排序对象的数据容器。

C STL中标准关联容器set, multiset, map, multimap内部采用的就是一种非常高效的平衡检索二叉树:红黑树,也称为RB树(Red-Black Tree)。RB树的统计性能要好于一般平衡二叉树,所以被STL选择作为了关联容器的内部结构。

对于mapset这种关联容器来说,不需要做内存拷贝和内存移动。以节点的方式来存储,其节点结构和链表差不多。

博主当前的CPP版本为c 14,所以相关源码内容就是以这个版本的为准。

Set定义

#include<set> usingnamespacestd; set<int>s1;//空对象 set<int>s2{3 4 2 1};//列表清单 默认less递增 输出为{1 2 3 4} set<int greater<int>>s3{6 5 7 8};//列表清单 输出为{8.7.6.5} Set常规操作

支持正向和反向迭代器,但是不支持随机迭代器访问元素。

C 中文在线手册:https://zh.cppreference.com/

begin()返回指向第一个元素的迭代器clear()清除所有元素count()返回某个值元素的个数,0或1,可以用来判断是否存在某数empty()如果集合为空,返回trueend()返回指向最后一个元素的迭代器equal_range()返回集合中与给定值相等的上下限的两个迭代器erase()删除集合中的元素
set集合中的删除操作是不进行任何的错误检查的,比如定位器的是否合法等等,所以用的时候自己一定要注意。find()返回一个指向被查找到元素的迭代器;如果没有找到,返回指向集合最后一个元素的迭代器。get_allocator()返回集合的分配器insert()在集合中插入元素
可以在集合中插入其他数组中指定个数的值lower_bound()返回指向大于(或等于)某值的第一个元素的迭代器key_comp()返回一个用于元素间值比较的函数max_size()返回集合能容纳的元素的最大限值rbegin()返回指向集合中最后一个元素的反向迭代器rend()返回指向集合中第一个元素的反向迭代器size()集合中元素的数目swap()交换两个集合变量upper_bound()返回大于某个值元素的迭代器value_comp()返回一个用于比较元素间的值的函数

增加元素insert插入

允许多个元素的插入,使用迭代器指定位置。

/* *insert能插入多个,慢但是实用 **/ voidadd1(){ set<int>demo{1 2}; //在第一个元素后面插入3 demo.insert(demo.begin() 3);//{1 2 3} 结果遵循递增规则 //直接插入元素 也是按照规则排列 demo.insert(-1);//{-1 1 2 3} //C 11之后 可以用emplace_hint或者emplace替代 //插入其他容器的部分序列 vector<int>test{7 8 9}; demo.insert( test.begin() --test.end());//{-1 1 2 3 8} //插入初始化列表 demo.insert({10 11});//{-1 1 2 3 8 10 11} set<int>s=demo; set<int>::iteratoriter; for(iter=s.begin();iter!=s.end(); iter){ cout<<*iter<<""; } } emplace插入

注意是每次只能插入一个,而且是只有构造函数,效率高!

/* *emplace,只能插入一个 *而且如果元素已经存在,是不会再次插入的 **/ voidadd2(){ set<int>demo{1 2}; demo.emplace(4);//{1 2 4} demo.emplace(4);//还是{1 2 4} set<int>s=demo; set<int>::iteratoriter; for(iter=s.begin();iter!=s.end(); iter){ cout<<*iter<<""; } } 遍历元素

/* *直接用迭代器,注意const_iterator还是iterator **/ voidsearch(){ set<int>demo{1 2}; //如果参数为constvector<int>需要用const_iterator //vector<int>::const_iteratoriter=v.begin(); set<int>s=demo; set<int>::iteratoriter; for(iter=s.begin();iter!=s.end(); iter){ cout<<*iter<<""; } } 删除元素

/* *删除有两种方式, *clear是直接清空 *erase是删除指定迭代器范围内的数字 *也可以用来删除指定的单个元素 **/ voiddel(){ set<int>demo{1 2 3 4 5}; //清空 demo.clear();//{} if(demo.empty()){//判断Vector为空则返回true demo.insert({6 7 8 9 10 11});//{6 7 8 9 10 11} //删除第一个元素 demo.erase(demo.begin());//{7 8 9 10 11} //删除指定元素 demo.erase(11); //删除前三个 demo.erase(demo.begin() next(demo.begin() 3));//{10 11} //只保留最后一个 demo.erase(demo.begin() demo.end());//{11} } set<int>s=demo; set<int>::iteratoriter; for(iter=s.begin();iter!=s.end(); iter){ cout<<*iter<<""; } } 比较函数元素为基本类型使用函数对象

当元素是基本类型,一般是变动规则为递增或者递减。

推荐使用自带的less或者greater函数比较。

//基础类型,变动规则为递增或者递减 voidBasicCompare(){ set<int less<int>>s2{6 5 7 8};//小数靠前。递增 输出为{5 6 7 8} set<int greater<int>>s3{6 5 7 8};//大数靠前。递减 输出为{8 7 6 5} //遍历查看内容 set<int>s=s2; set<int>::iteratoriter; for(iter=s.begin();iter!=s.end(); iter){ cout<<*iter<<""; } } 类外重载括号

当元素是基本类型,变动规则后不能用自带的less或者greater函数比较,就只能新建一个结构体来描述规则。这种方式是适用性最广的。

//基础类型,但是有特殊的比较规则 //此处以递增递减为例 //当然也可以指定为其他自定义类型 structSpecial{ booloperator()(constint&a constint&b){ //returna>b;//等价greater<int> returna<b;//等价less<int> } }; //基础类型需要类外重载 voidSpecialCompare(){ set<int Special>s2; s2.emplace(3); s2.emplace(2); s2.emplace(1); s2.emplace(4); //遍历查看内容 set<int Special>s=s2; set<int>::iteratoriter; for(iter=s.begin();iter!=s.end(); iter){ cout<<*iter<<""; } } /*1234*/ 元素为自定义类型

当元素是自定义结构体,可以在类外重载括号();也可以在类内重载小于号<,例子如下。

类内重载小于号

//自定义结构体 structInfo{ stringname; floatscore; Info(){ name="a"; score=60; } Info(stringsname floatfscore){ name=sname; score=fscore; } //类内重载方法 booloperator<(constInfo&a)const{ /*名字递减;若名字相同,则分数递减*/ if(a.name<name) returntrue; elseif(a.name==name){ returna.score<score; }else returnfalse; } }; voidInfoCompare(){ pair<set<Info>::iterator bool>result;//获取插入的结果 set<Info>s; //插入默认对象和指定对象 s.insert(Info()); s.insert(Info("a" 53)); s.insert(Info("keen" 68)); result=s.insert(Info("keen" 60)); //遍历查看内容 for(autoiter=s.begin();iter!=s.end(); iter){ cout<<iter->name<<""<<iter->score<<endl; } cout<<"插入元素的信息:"<<result.first->name<<""<<result.first->score<<endl; cout<<"插入是否成功"<<boolalpha<<result.second<<endl; //再次插入相同元素 result=s.insert(Info("keen" 60)); cout<<"插入元素的信息:"<<result.first->name<<""<<result.first->score<<endl; cout<<"插入是否成功"<<boolalpha<<result.second<<endl; } /* keen68 keen60 a60 a53 插入元素的信息:keen 60 插入是否成功true 插入元素的信息:keen 60 插入是否成功false */ 类外重载括号

#include<iostream> #include<set> usingnamespacestd; /*Student结构体*/ structStudent{ stringname; intage; stringsex; Student(){} Student(conststring&name intage conststring&sex):name(name) age(age) sex(sex){} }; /* *为Student指定排序准则 *先比较名字;若名字相同 则比较年龄。小的返回true *如果想要部分参数匹配,可以用正则表达式来规定 **/ classStudentCompare{ public: booloperator()(constStudent&a constStudent&b)const{ if(a.name<b.name) returntrue; elseif(a.name==b.name){ if(a.age<b.age) returntrue; else returnfalse; }else returnfalse; } }; intmain(){ set<Student StudentCompare>stuSet; stuSet.insert(Student("张三" 13 "male")); stuSet.insert(Student("李四" 23 "female")); /*构造一个用来查找的临时对象 可以看到 即使stuTemp与stu1实际上并不是同一个对象 *但当在set中查找时 仍能够查找到集合中的元素成功。 *这是因为已定义的StudentCompare的缘故。 */ StudentstuTemp; stuTemp.name="张三"; stuTemp.age=13; set<Student StudentCompare>::iteratoriter; iter=stuSet.find(stuTemp); if(iter!=stuSet.end()){ cout<<(*iter).name<<""<<(*iter).age<<""<<(*iter).sex<<endl; }else{ cout<<"Cannotfinethestudent!"<<endl; } return0; } 查找函数count统计元素个数

count函数是用来统计一个元素在当前容器内的个数。由于Set的特性,所以只能返回1或者0。

/* *用count函数寻找元素, **/ voidfind1(set<int>s){ if(s.count(4)==1){ cout<<"元素4存在"<<endl; } if(s.count(8)==0){ cout<<"元素8不存在"; } }

追查源码,我发现他是用的红黑树的__count_unique方法,从根节点开始比对,如果要查找的元素是比当前节点的数值要小就去左子树找,如果要查找的元素是比当前节点的数值要大就去右子树找,当匹配到节点值相等时,就返回1;当到最后的叶子结点仍然没有找到就返回0。

底层是使用的红黑树,所以查找起来还是很方便的。

源码如下。

size_type__tree<_Tp _Compare _Allocator>::__count_unique(const_Key&__k)const { __node_pointer__rt=__root(); while(__rt!=nullptr) { if(value_comp()(__k __rt->__value_)) { __rt=static_cast<__node_pointer>(__rt->__left_); } elseif(value_comp()(__rt->__value_ __k)) __rt=static_cast<__node_pointer>(__rt->__right_); else return1; } return0; } find获取元素迭代器

/* *用find函数寻找元素, **/ voidfind2(set<int>s){ if(s.find(4)!=s.end()){ cout<<"元素4存在"<<endl; }else{ cout<<"元素4不存在"; } if(s.find(8)!=s.end()){ cout<<"元素8存在"<<endl; }else{ cout<<"元素8不存在"; } }

追查源码,我发现他是用的红黑树的find方法,从根节点开始比对,如果要查找的元素是比当前节点的数值要大就去右子树找;如果查找的元素比当前节点的数值要小于等于匹配到节点值时,就赋值一次结果节点并去左子树找。一直到最后所查找的节点为空再结束。最终返回结果节点。

源码如下。

iterator__lower_bound(const_Key&__v __node_pointer__root __iter_pointer__result) { while(__root!=nullptr) { if(!value_comp()(__root->__value_ __v)) { __result=static_cast<__iter_pointer>(__root); __root=static_cast<__node_pointer>(__root->__left_); } else __root=static_cast<__node_pointer>(__root->__right_); } returniterator(__result); }

暂时看来,两个方法的底层实现逻辑是相似的,都是用平衡二叉树的方式去寻找节点。

区别是count返回1或0来标明元素是否存在;find函数是返回指针可以方便下一步修改或者取用。

感谢

谨以此文,送给未来笨兮兮又回来看的博主自己

哦,我看到了,过去那个很努力但是很皮的我哦

感谢现在努力的自己。

感谢现在的好奇,为了能成为更好的自己。

离线下载链接

C 中set用法详解

C STL set::find的用法

C STL set容器完全攻略

猜您喜欢: