C++中 set的用法
前言:
今天咱们继续来聊聊c++中的set。
上次的文章c++ set到底是什么遗留了一个问题没有回答,有些小伙伴有些疑问。就是为什么说set是关联式的容器,这个关联体现在哪里。
其实很简单,我们说过set的内部使用了红黑树对所有的元素进行了排序。在树结构当中,我们通常使用的都是<key, value>的形式。其中的key用来排序,value
则是我们实际存储的值。只不过set
有些特殊,它的value
和key
是一样的,相当于是<key, key>
的形式,所以它依然是关联式的容器。
今天这篇文章主要来聊聊set
的api
以及一些特殊的用法。
1、创建set
首先是set
容器的类模板定义。
template < class t, // 键 key 和值 value 的类型 class compare = less<t>, // 指定 set 容器内部的排序规则 class alloc = allocator<t> // 指定分配器对象的类型 > class set;
其中第一个参数表示set当中元素的类型,第二个参数则是set
容器内部的排序规则,第三个参数可以忽略,一般用不到。
set
有3种构造函数,可以应用在不同的场景当中,我们简单来列举一下。
1.1 方法1
set<string> st;
最常规的一种,没有任何参数,直接创建。
1.2 方法2
set<string> st{"good", "bad", "medium"};
直接通过花括号枚举我们要传入set
的值。
1.3 方法三
set<string> st{"good", "bad", "medium"}; set<string> st2(st);
拷贝创建,从另外一个set
当中拷贝元素。
除了这三种形式的构造函数之外,还可以利用set类模板的第二个参数,传入元素排序规则来影响set中元素的排序,这勉强也算
是一种构造方法:
set<string, greater<string>> st{"good", "bad", "medium"};
我们不传入greater
的排序结果是"bad
", "good
", "medium
",当我们传入了这个参数之后,结果会变成:"medium
", "good
", "bad
"。
这是因为我们传入的排序规则重新定义了元素的大小关系。
2、使用set
创建完了set就需要使用,使用无非增删改查。
我们先来说说增,往set
里添加元素的函数有好几个,我们一个一个来说。
2.1 insert
insert
函数非常简单,就直接调用,往set里插入即可。
st.insert("hhh");
但insert还可以批量插入多个元素:
st.insert({"hhh", "wow"});
2.2 emplace
emplace
函数的功能和insert
一样,可以往set
当中插入元素。它和insert
最大的区别在于emplace
传入的参数并不是要插入的元素,而是构造元素需要的参数。
我这么说估计有点难理解,其实很简单,我们来对比一下就知道了。
假设我们有一个set它的类型是结构体p,当中我们重载了它的比较算子,这个先忽略。
struct p { int x, y; p(int x, int y) : x(x), y(y){}; bool operator<(const p b) const { return this->x < b.x; } }; set<p> st;
如果我们要使用insert
应该怎么操作呢?
p p{0, 3}; st.insert(p);
如果使用emplace函数呢,则是这样:
st.emplace(1, 23);
因为emplace
的内部会替我们去调用结构体p的构造函数,使用1和23这两个参数构造出一个p的实例来存入set当中。
使用emplace
可以节省掉创建实例的一步,所以通常工程当中往往大量使用emplace
。
emplace
函数返回的结果是一个pair
,pair
的第一个元素是set
的迭代器,表示插入的元素的位置,第二个值是一个bool
,表示是否插入成功。
2.3 emplace_hint
emplace
函数的改进版,接受额外的参数表示插入set的位置。它的返回结果也有了一些变化,返回的是一个迭代器。
如果插入成功则返回新添加的元素,否则则指向set
容器中和添加元素相同的元素。
使用emplace_hint
会影响set中的有序性,一般不建议使用。
2.4 erase
说完了插入再说说删除,在set当中删除的方法只有一个就是erase
,但是它却有好几种用法。
我们直接来看它的函数签名:
size_type erase (const value_type& val); iterator erase (const_iterator position); iterator erase (const_iterator first, const_iterator last);
第一种方法我们传入了一个val值,也就是我们要删除的元素。
第二种方法我们传入的是一个迭代器,它会删除迭代器指向的元素。第三种方法类似,只不过我们传入的是两个迭代器,表示一个范围,它会删除这个范围内所有的元素。
第一种方法的返回值是一个整数,表示删除的元素个数。后面两种返回的都是一个迭代器,指向删除元素后面一个位置。
2.5 clear
清空set。
2.6 find
set中的查询函数,传入我们要查询的value
,返回一个迭代器。
set<string>::iterator it = st.find("good");
如果成功找到则返回指向该元素的迭代器,否则指向end
。
2.7 count
同样是查询函数,只不过它返回的不再是迭代器,而是一个整数,表示查询到元素的个数。
int cnt = st.count("good");
2.8 lower_bound 和 upper_bound
lower_bound
和upper_bound
严格也算是查询函数,只不过它们查询的范围。lower_bound
查询的是set当中第一个大于等于val的位置,而upper_bound
查询的是set中第一个严格大于val的位置。
set<string>::iterator it_low = st.lower_bound("i"); set<string>::iterator it_up = st.upper_bound("i");
同样这两个函数返回的是一个迭代器。
2.9 equal_range
这个函数返回的是一个pair
,它的第一个元素是lower_bound
的结果,第二个元素是upper_bound
的结果。
pair<set<string>::iterator, set<string>::iterator> ret = st.equal_range("i");
3、总结
到这里,关于set
常用的方法基本上就都介绍完了,除此之外还有一些其他细枝末节的方法就不赘述了。比如像是size(),max_size()
等等,大家有用到去查询即可。
但是有一个疑问不知道大家有没有发现,就是我们没有介绍到修改的函数。是set
不支持修改吗?
关于这个问题的答案并不是老梁故意卖关子,而是它非常复杂,一句两句很难说清楚,老梁将在下一篇文章当中好好探讨一下这个问题。如果大家有修改元素的需求,可以用erase + insert
代替。
到此这篇关于c++中 set的用法的文章就介绍到这了,更多相关c++ set用法内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!