Ⅰ . Set 类
01 Set 介绍
template < class T, // set::key_type/value_type
class Compare = less<T>, // set::key_compare/value_compare
class Alloc = allocator<T> // set::allocator_type
> class set;
通过插入新的元素来扩展容器,通过插入的元素数量来增加容器的大小。
因为集合中的元素是唯一的,插入操作会检查每个插入的元素是否等同于已经存在于容器中的元素。
如果是,则不插入该元素,返回到这个现有元素的迭代器。
在内部,集合容器保持着其所有元素按照比较对象的标准进行排序,这些元素总是按照这个排序插入到它各自的位置, Set 是 排序 + 去重。
官方文章介绍:https://cplusplus.com/reference/set/set/
Set 底层是平衡二叉搜索树,是基于红黑树实现的
02 元素的插入
下面我们使用 insert 接口,来实现一下 Set 的插入
pair<iterator,bool> insert (const value_type& val);
iterator insert (iterator position, const value_type& val);
template <class InputIterator>
void insert (InputIterator first, InputIterator last);
代码演示:
void test_set1()
{
set<int> s;
s.insert(4);
s.insert(8);
s.insert(5);
s.insert(1);
s.insert(3);
s.insert(3);
set<int>::iterator it = s.begin();
while (it != s.end())
{
cout << *it << " ";
++it;
}
cout << endl;
}
int main()
{
test_set1();
return 0;
}
运行结果如下:
我们可以 发现 Set 确实具有去重的效果,这和集合是一样的,集合元素具有唯一性。
所以,如果要插入的元素是已经存在于 Set 中的,就不会再插入了。
同时,Set 还会自动排好序,默认是按升序排列的。
03 Set 支持范围 for
既然 Set 支持迭代器,那自然也是支持范围 for 的,毕竟从底层的角度来说,范围 for 其实就是迭代器。
代码演示:
void test_set1()
{
set<int> s;
s.insert(4);
s.insert(8);
s.insert(5);
s.insert(1);
s.insert(3);
s.insert(3);
for (auto e : s)
{
cout << e << " ";
}
//set<int>::iterator it = s.begin();
//while (it != s.end())
//{
// cout << *it << " ";
// ++it;
//}
cout << endl;
}
运行结果如下:
04 元素的查找
再介绍一下 find 接口,如果这个元素被找到就会返回 val 的迭代器,否则就会返回 end。
iterator find (const value_type& val) const;
代码演示:
void test_set2()
{
set<int> s;
s.insert(4);
s.insert(8);
s.insert(5);
s.insert(1);
s.insert(3);
s.insert(3);
set<int>::iterator pos = s.find(5);
if (pos != s.end())
{
cout << "找到了" << endl;
}
}
运行结果如下:
05 判断元素是否在集合中
有时我们需要判断元素在不在集合中,使用 count 往往比 find 更方便。
size_type count (const value_type& val) const;
在集合中则返回1,不在则返回0。
代码演示:
void test_set3()
{
set<int> s;
s.insert(4);
s.insert(8);
s.insert(5);
s.insert(1);
s.insert(3);
s.insert(3);
if (s.count(5))
{
cout << "存在" << endl;
}
}
运行结果如下:
这里如果使用 find 的话,还需要判断 s.find(5) != s.end()。
06 元素的删除
erase 支持三种,分别是在某个位置删除,删除一个值和删除一个区间。
void erase (iterator position);
size_type erase (const value_type& val);
void erase (iterator first, iterator last);
代码演示:
void test_set4()
{
set<int> s;
s.insert(4);
s.insert(8);
s.insert(5);
s.insert(1);
s.insert(3);
s.insert(3);
cout << "当前集合元素: ";
for (auto e : s) {
cout << e << " ";
}
cout << endl;
int x = 0;
while (1) {
cout << "请输入要删除的元素:";
cin >> x;
// 查看要删除的元素在不在集合中
set<int>::iterator pos = s.find(x);
if (pos != s.end()) {
// 在集合中,删除
s.erase(pos);
cout << "成功删除: " << x << endl;
// 打印删除后的结果
cout << "删除后: ";
for (auto e : s) {
cout << e << " ";
}
cout << endl;
}
else {
// 不在集合中,提示删除失败
cout << "删除失败!" << x << "不在集合中。" << endl;
}
}
}
运行结果如下:
也可以直接给 erase 一个值进行删除,这个用法比较简单。
代码演示:
void test_set5()
{
set<int> s;
s.insert(4);
s.insert(8);
s.insert(5);
s.insert(1);
s.insert(3);
s.insert(3);
cout << "当前集合元素: ";
for (auto e : s)
{
cout << e << " ";
}
cout << endl;
s.erase(3);
cout << "删除后: ";
for (auto e : s)
{
cout << e << " ";
}
cout << endl;
}
运行结果如下:
07 区间查找:lower_bound 和 upper_bound 接口
iterator lower_bound(const key_type& key);
lower_bound() 用于在指定区域内查找不小于目标值的第一个元素,返回一个指向集合中第一个大于等于给定值的元素的迭代器,如果没有找到,则返回 end
代码演示:用 lower_bound 查找第一个不小于 25 的元素
int main()
{
set<int> s = { 10,20,30,40,50 };
auto it = s.lower_bound(25);
if (it != s.end()) {
cout << "第一个不小于 25 的元素是: " << *it << endl;
}
else {
cout << "未找到不小于 25 的元素" << endl;
}
return 0;
}
运行结果如下:
lower_bound(25) 会返回一个指向集合中第一个不小于 25 的迭代器。在上面的集合中,第一个不小于 25 的元素是 30,所以输出的结果是 30
这个可以用来干什么呢?如果我们想删除大于等于 x 的所有元素,我们就可以用它和区间删除的办法来实现。
代码演示:删除大于等于 x 的所有元素
void test_set6()
{
set<int> s = { 10, 20, 30, 40, 50 };
cout << "删除前: ";
for (auto e : s)
{
cout << e << " ";
}
cout << endl;
int x = 0;
cout << "请输入x: ";
cin >> x;
auto it = s.lower_bound(x); // 找到第一个不小于x的元素
s.erase(it, s.end()); // 迭代器区间删除
cout << "删除后: ";
for (auto e : s)
{
cout << e << " ";
}
cout << endl;
}
运行结果如下:
接收到 x 的值为 30 之后,从 lower_bound 的位置开始作为迭代器的起始位置,end 作为结束位置,调用迭代器版本的 erase 就可以实现 30,40,50 全部删除的效果。
另外一个 upper_bound 接口,则是在指定范围内查找大于目标值的第一个元素,不包含等于。
代码演示:
void test_set7()
{
set<int> s = { 10, 20, 30, 40, 50 };
cout << "删除前: ";
for (auto e : s)
{
cout << e << " ";
}
cout << endl;
int x = 0, y = 0;
cin >> x >> y;
auto left_it = s.lower_bound(x);
auto right_it = s.upper_bound(y);
s.erase(left_it, right_it);
cout << "删除后: ";
for (auto e : s)
{
cout << e << " ";
}
cout << endl;
}
运行结果如下:
Ⅱ . multiset 类
01 不去重的 Set
如果我们想让 Set 不去重,我们可以使用 multiset,使用方法和 Set 大差不差。
Set 是排序 + 去重,但 multiset 是排序 + 不去重,也就是说 multiset 允许元素重复
02 multiset 的用法
multiset 就在 Set 类中,所以头文件就是 #include <set>
代码演示:multiset 的插入
void test_set8()
{
multiset<int> s;
s.insert(4);
s.insert(8);
s.insert(5);
s.insert(1);
s.insert(3);
s.insert(3);
for (auto e : s)
{
cout << e << " ";
}
cout << endl;
}
运行结果如下:
可以看到,是不去重的,就给你排个序,已经有的元素也会继续插入。
03 multiset 的删除
因为 Set 不重复,所以 erase 就删除指定元素,但 multiset 是允许冗余的。
所以 multiset 的 erase 接口会把所有指定元素删掉:
void test_set9()
{
multiset<int> s;
s.insert(4);
s.insert(8);
s.insert(5);
s.insert(1);
s.insert(3);
s.insert(3);
cout << "删除前: ";
for (auto e : s)
{
cout << e << " ";
}
cout << endl;
s.erase(3); // 会把所有的3都杀了
cout << "删除后: ";
for (auto e : s)
{
cout << e << " ";
}
cout << endl;
}
运行结果如下:
04 multiset 的查找
multiset 的 find 如果要查找的元素在集合中有多个,会返回中序的第一个,假设我们要 find(3):
代码演示:
void test_set10()
{
multiset<int> s;
s.insert(4);
s.insert(8);
s.insert(5);
s.insert(1);
s.insert(3);
s.insert(3);
s.insert(3);
for (auto e : s)
{
cout << e << " ";
}
cout << endl;
// 返回的是中序的第一个
auto pos = s.find(3);
while (pos != s.end())
{
cout << *pos << " ";
}
}