C++中STL模板中set的用法总结

2 篇文章 0 订阅
1 篇文章 0 订阅

本文从一名大学生新生的角度出发,简单总结了C++中STL模板set的用法,希望可以帮到任何看到这篇文章的人。


前言

作者的身份是一名普通的大一计科学生,对于cpp的理解浅薄,还请批评指出。
可能这篇文章更适用于初高中的oier~重在如何使用而非严谨的底层原理


 一、set是什么?

其实set翻译过来就是集合,在高中数学我们知道集合具有互异性,也就是同一个元素只会出现一次。
同样的,set 作为c++中STL容器,它的最大特点就是具有互异性,我们一般可以用来快速判断某个元素是否存在。


二、set的简单介绍与特性说明

1.简单理解的原理

set正式名字叫做关联容器,它的关联性在于它内部是用红黑树对所有元素排序的

Stop!这里因此就有一个特性:set是默认按升序顺序排序好的

一般来说树结构存在<key,value>形式,其中key用于排序,value为其值,只是在set中value与key

值是一样的,但它仍然属于关联容器

 2.一些其他需要留意的特性

  1. set的插入不需要构造键值对,只需要插入value
  2. set元素不重复,因此可用于去重
  3. 使用set迭代器遍历,可得到升序(默认)序列
  4. set中元素默认按小于比较
  5. set元素支持增删查,不允许修改
  6. 查找某个元素的时间复杂度为O(\log_{2}N)

三、set的用法

在这里,我们都假设我们声明的set的变量名为P

1.set的构造函数

共有三种方式

①直接创建不带参数

set<typename>Variable_name

eg: 

set<int>P

 ②通过花括号传入初始值

set<typename>Variable_name{x1,x2,x3,……,xn}

eg1. 

set<int>P{2,3}

 eg2.

set<string>P{"LHP","YYT"}

 ③从另外一个set中拷贝元素

set<string> st{"good", "bad", "medium"};
set<string> st2(st);

在定义set时,我们可以通过传入元素排序规则(set中的第二个参数)来改变排序方式,比如

set<string, greater<string>> st{"good", "bad", "medium"};

 在这里greater代表字典序更大的排在前面,我们没传入此参数遍历结果为"bad","good","medium"

而传入了greater规则后结果则为"medium","good","bad".

一种值得一提的构造方式是将STL中的pair与set一同结合起来使用

set<pair<int,int>>P

这样我们便可以判定一对值是否存在于set中(经常可用于判断一个坐标是否已经经过)

注意插入时我们用到make_pair()语句

Set_Name.insert(make_pair(x,y))

2.set的begin()与end()函数 

要讲这个就得先讲迭代器

set<typename>::iterator it;
set<string>::iterator it;

我们可以简单的将迭代器暂时理解为指针

也因为如此理解,*it代表对应地址的值,因此采用这种方法访问元素

P.begin()返回第一个元素的迭代器 

P.end()返回最后一个元素下一个位置的迭代器


3.set的遍历

①正序遍历

因此,想要遍历P,则可以如此写

set<string>::iterator it;
for(it=P.begin();it!=P.end();it++){
    cout<<*it<<endl;
}

还可以利用auto自动识别数据类型来偷懒,见下

for(auto it=P.begin();it!=P.end();it++){
    cout<<*it<<endl;
}

 在这里我们需要注意终止条件为 it!=P.end(),原因是当it为P.end()时,此时指向的地址为最后一个元素的下一位,自然需要终止。

我们同样可以用while循环来写,如何写与for循环大同小异,在此略去。

值得一提的是,我们还可以用智能指针来遍历集合,这里是两个例子:

(后面有空我会学习下智能指针,可能会单独开个文章讲讲,又是一个坑)

set<pair<int, int>>P;
P.insert(make_pair(2, 3));
P.insert(make_pair(5, 2));
for (pair<int, int> p : P) {
	cout << p.first << " " << p.second << endl;
}
set<string>P{"LHP", "YYT"};
P.insert("XXX");
for (string p : P) {
	cout << p << endl;
}
②逆序遍历

与正序遍历类似,stl中自带逆序遍历的函数

类似的,rbegin()代表逆序遍历的第一个元素的迭代器,rend()代表逆序遍历最后一个元素下一个地址的迭代器

注意:在定义迭代器时候相应的,我们应该使用 ::reverse_iterator 而非 ::iterator 以代表反向迭代器

	set<int> P{1, 2, 3, 4, 5};
	set<int>::reverse_iterator it;
	for (it = P.rbegin(); it != P.rend(); it++)
		cout << *it << " ";
//	输出分别为 5 4 3 2 1


4.set的插入元素的函数

 ①.set中的insert()函数

简单易懂的用法,括号内填入声明数据类型的值便好

例子如下:

set<string>P;
P.insert("LHP");
set<int>L;
L.insert(666);

insert()还可以同时插入多个元素,例子见下(我觉得作用也就节省代码量吧)

set<string>P;
P.insert({"LHP","YYT"});
set<int>L;
L.insert({666,999});

 你若是跟STL中的pair同时使用,则需要用到make_pair()插入

set<pair<int,int>>P;
P.insert(make_pair(2,3));
②set中的emplace()函数

 与insert一样同样具有插入元素的功能,但不一样的是,emplace()传入的是构造函数的对应参数而不是元素本身,这样说可能有点不明白,我们用实例去理解:

struct Node {
	int score;
	string Name;
	Node(int inscore, string inName): score(inscore), Name(inName) {}
	bool operator<(const Node b) const  {
		return this->score < b.score;
	}
};
set<Node>P;
P.emplace(100, "LHP");

这里我们重载了比较算子,在这里我们先略去不提。

我们留意到,emplace内部会帮我们调用结构体构造函数,我们无需自己构造,可以节省创建实例的步骤,因此在工程中emplace往往会大量使用。

emplace()函数返回一个pair,第一个参数为set迭代器,代表插入地址,第二个参数为一个bool值,代表是否成功插入


5.set的删除erase()函数

删除只有erase()一个函数,但是其有三种用法 

①.删除对应元素

括号内填入删除元素

例子:

set<string>P;
P.insert("666");
P.erase("666");

返回一个整数,代表删除元素的个数 

②.删除迭代器对应的元素

括号内填set的迭代器

set<string>P;
P.insert("666");
set<string> ::iterator it = P.begin();
P.erase(it);

返回的迭代器为删除元素的后一个位置 

③.删除两个迭代器范围内的所有元素
set<string>P{"11", "22", "33"};
set<string> ::iterator it1 = P.begin(), it2 = P.end();
P.erase(it1, it2);

 注意:这里第二个参数传入的代表删除末尾元素的下一位,it2这个位置是不被删除的!

④清空set
P.erase()

 或者很无聊地利用上面③中的代码一样可以达到清空set的目的


6.查找元素是否存在与位置

①find()函数

set中的查询函数,传入我们要查询的value,返回一个迭代器。

如果找到该元素,则返回该元素的地址的迭代器,如果没找到,则返回end()值

P1.find("LHP");
P2.find(666);
P3.find(make_pair(2,3));
②count()函数

返回对应元素的个数,实际上由于集合内元素的特异性,返回的非0即1(1表示存在,0表示无)

我是觉得没有什么卵用,不如用find()应用场景更广

P1.count("LHP");
P2.count(666);
P3.count(make_pair(2,3));
 ③查找大于等于和严格大于某值的元素(lower_bound()与upper_bound()函数)

括号内填入元素

lower_bound()返回第一个大于等于val的迭代器

upper_bound()返回第一个严格大于val的迭代器

注意:这里很容易弄混,请记牢

	set<int> P{1, 2, 3, 4, 5};
	cout << *P.lower_bound(2) << endl;
	cout << *P.upper_bound(2) << endl;
//	输出分别为 2 3
④兼得lower_bound与upper_bound的函数equal_range()

另外一种函数叫做equal_range()可以返回一个pair,pair的第一个为lower_bound()迭代器,第二个则为upper_bound()的迭代器

例子如下:

	set<int> P{1, 2, 3, 4, 5};
	auto it = P.equal_range(2);
	cout << *it.first << " " << *it.second << endl;
//	输出分别为 2 3

这里图省事用了auto

第一个返回的是lower_bound(),第二个返回的是upper_bound()

访问这个pair中的元素注意是迭代器,访问迭代器内容还要用指针的“ * ”


7.其他函数(empty()和size())

P.empty()  返回是否为空,是则为1,不是则为0
P.size()   返回集合的大小(几个元素


总结与后文

花了两个晚上去写这个小总结,参考了许多文档,感谢这些前辈。

果然总结是有许多好处的,对于set原来一知半解的我在写完博客后完全透析了,果然输出才是最好的学习方法。

关于set的暂时就告一段落了,其实还有类似emplace_hint()的函数,但在这里不加介绍,另外实际上还有 mulset 这一STL容器,可以看看在未来进行文章再总结用法。

感谢你看到这里,希望这篇文章对你的学习工作有所帮助,如果有任何意见,麻烦写到评论区,我会好好采纳并修改。

参考资料:

详解c++---set的介绍_c++ set_叶超凡的博客-CSDN博客

深入浅出C++ ——set类深度剖析_c++ set_程序员Jared的博客-CSDN博客

建议收藏!C++ set用法大全-腾讯云开发者社区-腾讯云

C++中的Set的用法整理_c++ set_菜到极致就是渣的博客-CSDN博客

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值