【C++】set详解

📢博客主页:https://blog.csdn.net/2301_779549673

📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正!

📢本文由 JohnKi 原创,首发于 CSDN🙉

📢未来很长,值得我们全力奔赴更美好的生活✨

文章目录


📢前言

Set 是 C++ 标准模板库(STL)中的一种关联容器,主要用于存储不重复且有序的元素。其内部实现采用红黑树,这种数据结构具有自动排序的特性,能够高效地进行插入、删除和查找操作。

红黑树是一种平衡二叉搜索树,它的统计性能优于一般的平衡二叉树。在 set 中,每个元素的值都唯一,并且元素的值不能直接被改变。这是因为 set 中的元素值就是其键值,关系到 set 元素的排列规则。如果任意改变 set 的元素值,会严重破坏 set 的组织。

Set 的迭代器被定义为底层红黑树的 const_iterator,杜绝了写入操作。这意味着我们不能通过 set 的迭代器直接修改元素的值。例如,当我们尝试通过 set 的迭代器改变元素的值时,编译器会报错。

此外,当对 set 进行元素新增操作(insert)或删除操作(erase)时,操作之前的所有迭代器,在操作完成之后都依然有效,被删除的那个元素的迭代器必然是个例外。这种特性使得 set 在进行动态操作时,能够保持迭代器的有效性,方便了程序的编写和维护。

总的来说,C++ 中的 set 容器以其独特的特性和高效的内部实现,为程序员提供了一种方便、可靠的数据存储和操作方式。


🏳️‍🌈 一、set类的介绍

  • set的声明如下,T就是set底层关键字的类型.
  • set默认要求T支持小于比较,如果不支持或者想按自己的需求走可以自行实现仿函数传给第二个模版参数
  • set底层存储数据的内存是从空间配置器申请的,如果需要可以自己实现内存池,传给第三个参数。
  • 一般情况下,我们都不需要传后两个模版参数。
  • set底层是用红黑树实现,增删查效率是O(l0gN),迭代器遍历是走的搜索树的中序,所以是有序的。
  • 前面部分我们已经学习了vector/list等容器的使用,STL容器接口设计,高度相似,所以这里我们就不再一个接口一个接口的介绍,而是直接带着大家看文档,挑比较重要的接口进行介绍。
cpp 复制代码
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的构造和迭代器

  • set的构造我们关注以下几个接口即可。
  • set的支持正向和反向迭代遍历,遍历默认按升序顺序,因为底层是二叉搜索树,迭代器遍历走的中序;支持迭代器就意味着支持范围for,set的iterator和const iterator都不支持迭代器修改数据,修改关键字数据,破坏了底层搜索树的结构。

Set 的构造方式主要有以下几种:
默认构造函数 :set st;,创建一个空的 set。例如:set mySet;。
拷贝构造函数 :set(const set &st);,创建一个新的 set,它是现有 set 的副本。例如:set mySet = {1, 2, 3}; set anotherSet(mySet);。
使用区间构造 :可以用一个已有的区间中的元素构造 set,如set s2(s1.begin(), s1.end());。
赋值操作可以使用重载的等号操作符:set& operator=(const set &st);,将一个 set 的内容赋值给另一个已经存在的 set。例如:set mySet = {1, 2, 3}; set anotherSet; anotherSet = mySet;。

cpp 复制代码
// empty (1) ⽆参默认构造
explicit set (const key_compare& comp = key_compare(),
              const allocator_type& alloc = allocator_type());
// range (2) 迭代器区间构造
template <class InputIterator>
set (InputIterator first, InputIterator last,
     const key_compare& comp = key_compare(),
     const allocator_type& = allocator_type());
// copy (3) 拷⻉构造
set (const set& x);
// initializer list (5) initializer 列表构造
set (initializer_list<value_type> il,
     const key_compare& comp = key_compare(),
     const allocator_type& alloc = allocator_type());
// 迭代器是⼀个双向迭代器
iterator -> a bidirectional iterator to const value_type
// 正向迭代器
iterator begin();
iterator end();
// 反向迭代器
reverse_iterator rbegin();
reverse_iterator rend();

🏳️‍🌈三、set的增删查

set的增删查关注以下几个接口即可:

  • 插入元素
    使用insert方法可以在 set 中插入元素。例如:mySet.insert(5);。如果元素已存在,插入操作将被忽略。insert方法的返回值是个pair结构的对组,pair<iterator, bool>,bool 代表插入是否成功。当向 set 容器添加元素成功时,该迭代器指向 set 容器新添加的元素,bool 类型的值为 true;如果添加失败,即证明原 set 容器中已存有相同的元素,此时返回的迭代器就指向容器中相同的此元素,同时 bool 类型的值为 false。
  • 查找元素
    使用find方法查找 set 中元素。例如:auto it = mySet.find(element);,如果找到元素,it将指向该元素;否则,it将指向 set 的尾后。可以通过判断it是否等于end()来确定元素是否被找到。
  • 删除元素
    使用erase方法删除 set 中元素,有以下几种方式:
    删除迭代器指向的元素:erase(pos);,例如:auto it = mySet.find(3); if(it!= mySet.end()){ it = mySet.erase(it); }。
    删除指定值的元素:erase(elem);,例如:mySet.erase(4);。
    删除区间内的元素:erase(beg, end);。
  • 其他方法
    size():返回 set 中元素的数目。
    empty():判断 set 是否为空。如果为空,返回 true;否则返回 false。
    clear():清空 set 中的所有元素。
    lower_bound():返回大于或等于给定元素的第一个元素的迭代器。
    upper_bound():返回大于给定元素的第一个元素的迭代器。
    equal_range():返回一组迭代器,表示给定元素在 set 中的范围。
cpp 复制代码
Member types
key_type -> The first template parameter (T)
value_type -> The first template parameter (T)
// 单个数据插⼊,如果已经存在则插⼊失败
pair<iterator, bool> insert (const value_type& val);
// 列表插⼊,已经在容器中存在的值不会插⼊
void insert (initializer_list<value_type> il);
// 迭代器区间插⼊,已经在容器中存在的值不会插⼊
template <class InputIterator>
void insert (InputIterator first, InputIterator last);
// 查找val,返回val所在的迭代器,没有找到返回end()
iterator find (const value_type& val);
// 查找val,返回Val的个数
size_type count (const value_type& val) const;
// 删除⼀个迭代器位置的值
iterator erase (const_iterator position);
// 删除val,val不存在返回0,存在返回1
size_type erase (const value_type& val);
// 删除⼀段迭代器区间的值
iterator erase (const_iterator first, const_iterator last);
// 返回⼤于等val位置的迭代器
iterator lower_bound (const value_type& val) const;
// 返回⼤于val位置的迭代器
iterator upper_bound (const value_type& val) const;

易错点提示

end()函数的正确用法:end()函数的作用不是用于返回最后一个元素,而是用于返回 set 中最后一个元素的后一个位置的指针。如果一个 set 中的元素是<1,2,3>,那么返回的就是元素 3 之后的一位的指针。如果要看最后一个元素,可以使用遍历的方式找到最后一个元素,或者使用其他方法。

不能直接 copy:不能将一个 set 直接使用=号赋值给另外一个 set。如下面的写法就是错误的:set set1; set set2 = set1;。这种写法会导致编译错误或者不可预期的结果。在 C++ 中,set 的赋值操作需要使用特定的方法,如拷贝构造函数或者赋值运算符重载。

🏳️‍🌈四、insert和迭代器遍历使用样例

cpp 复制代码
#include<iostream>
#include<set>
using namespace std;
int main() {
// 去重+升序排序
    set<int> s;
// 去重+降序排序(给⼀个⼤于的仿函数)
//set<int, greater<int>> s;
    s.insert(5);
    s.insert(2);
    s.insert(7);
    s.insert(5);
//set<int>::iterator it = s.begin();
    auto it = s.begin();
    while (it != s.end()) {
        // error C3892: "it": 不能给常量赋值
// *it = 1;
        cout << *it << " ";
        ++it;
    }
    cout << endl;
// 插⼊⼀段initializer_list列表值,已经存在的值插⼊失败
    s.insert({ 2, 8, 3, 9 });
    for (auto e : s) {
        cout << e << " ";
    }
    cout << endl;
    set<string> strset = { "sort", "insert", "add" };
// 遍历string⽐较ascll码⼤⼩顺序遍历的
    for (auto& e : strset) {
        cout << e << " ";
    }
    cout << endl;
}

👥总结


本篇博文对 set 做了一个较为详细的介绍,不知道对你有没有帮助呢

觉得博主写得还不错的三连支持下吧!会继续努力的~

相关推荐
菜鸟学Python2 分钟前
Python 数据分析核心库大全!
开发语言·python·数据挖掘·数据分析
C++忠实粉丝3 分钟前
计算机网络socket编程(4)_TCP socket API 详解
网络·数据结构·c++·网络协议·tcp/ip·计算机网络·算法
一个小坑货9 分钟前
Cargo Rust 的包管理器
开发语言·后端·rust
bluebonnet2713 分钟前
【Rust练习】22.HashMap
开发语言·后端·rust
古月居GYH14 分钟前
在C++上实现反射用法
java·开发语言·c++
Betty’s Sweet17 分钟前
[C++]:IO流
c++·文件·fstream·sstream·iostream
敲上瘾30 分钟前
操作系统的理解
linux·运维·服务器·c++·大模型·操作系统·aigc
不会写代码的ys37 分钟前
【类与对象】--对象之舞,类之华章,共绘C++之美
c++
兵哥工控39 分钟前
MFC工控项目实例三十二模拟量校正值添加修改删除
c++·mfc
在下不上天39 分钟前
Flume日志采集系统的部署,实现flume负载均衡,flume故障恢复
大数据·开发语言·python