

本编主要介绍 set 的使用,multiset涉猎其与 set 不同处。
1、序列式容器与关联式容器的简单介绍
- 序列式容器:元素按线性有序位置存储,可通过位置访问,元素顺序与插入次序一致的容器;STL 标准序列式容器包含 array、vector、deque、list、forward_list等;序列式容器仅维持元素线性先后次序,元素本身无大小关联,交换元素仍合法。
- 关联式容器依靠键值key组织元素,元素次序由键key决定,与插入先后无关;依靠键管理元素,元素顺序由键key决定,和插入先后次序无关,可快速按键key查找;STL 关联式容器:set、multiset、map、multimap;无序关联容器:unordered_set、 unordered_multiset、unordered_map、unordered_multimap;关联式容器以键key维系元素逻辑关系,整体为非线性结构,随意交换元素会破坏内部有序规则。
本章节讲解的set底层是红黑树,红黑树是⼀颗平衡二叉搜索树。set是key搜索场景的结构。
2、set系列的使用
2.1、set类介绍
📌这里 set 创写的 "key_type" 和 "value_type" 是为了和 map 保持一致。
- set 模板首参数 T:代表 set 存储元素(关键字)的数据类型。
- 第二个模板参数 Compare:排序仿函数,默认使用小于比较
less<T>,所以用中序,默认是升序;如果不支持或者想按自己的需求走可以自行实现仿函数传给第二个模版参数。- 第三个模板参数 Allocator:空间配置器,负责节点内存申请释放;可自定义内存池实现,日常开发一般使用默认分配器。
- 参数使用习惯:绝大多数场景只需填写第一个模板参数,后两个参数省略,使用默认实现。
- 底层结构与性能:底层依托红黑树(平衡二叉搜索树);插入、删除、查找时间复杂度\(O(logN)\);迭代器遍历执行红黑树中序遍历,因此遍历结果天然有序。
- 📌总结:
std::set<T, Compare, Allocator>依靠红黑树存储唯一元素,依靠仿函数确定排序规则,依靠空间配置器管理内存,中序遍历保证有序,增删查找O(logN)。🧀为啥用一个比较运算符,就能比完所有的情况呢?
2.2、set的构造
2.3、set的迭代器
📌其迭代器是双向迭代器
📌迭代器失效:
auto pos = s.find(x); if (pos != s.end()) { // pos失效 s.erase(pos); //cout << *pos << endl; }
2.4、set的特性
- 去重 + 排序,增删查,没有改。
2.5、set的增删查
2.5.1、插入
列表插入,走的隐式类型转换 s.insert({ 2,8,3,9,2 }); // void insert (initializer_list<value_type> il); set<string> strset = { "sort", "insert", "add" }; set<string> strset({ "sort", "insert", "add" });
set<string> strset = { "sort", "insert", "add" };— 拷贝列表初始化:用=加花括号,编译器把{...}当作initializer_list<string>传给set的initializer_list构造函数。set<string> strset({ "sort", "insert", "add" });— 直接列表初始化:显式把{...}作为实参传进去,等价于调用set(initializer_list<string>),效果一样,只是没用=。
2.5.2、删除
📌删除⼀个迭代器位置的值。
📌删除val,val不存在返回0,存在返回1。📌删除⼀段迭代器区间的值
2.5.3、查找
// 算法库的查找 O(N) auto pos1 = find(s.begin(), s.end(), x); // set自身实现的查找 O(logN) auto pos2 = s.find(x);📌利用count间接实现快速查找 --- "在不在/有没有":
if (s.count(x)) { cout << x << "在!" << endl; } else { cout << x << "不存在!" << endl; }🍞set::count():查找目标元素,存在返回1,不存在返回0,无法获取迭代器。
2.6、lower_bound和upper_bound
lower_bound:返回指向第一个 ≥ val 的元素的迭代器。upper_bound:返回指向第一个 > val 的元素的迭代器。- 合起来就是左闭右开区间
[lower_bound, upper_bound),所有等于 val 的元素都落在这个范围里。这两个接口,可以有效的将一整段迭代器划分多段。
// 返回 >= 25 auto itlow = myset.lower_bound(25); // 返回 > 55 auto itup = myset.upper_bound(55); // 删除这段区间的值 myset.erase(itlow, itup);
3、multiset与set的差异
- multiset 不去重,纯排序。
相比set不同的是,x可能会存在多个,find查找中序的第一个。 multiset 会把所有等于 val 的全删掉(返回实际删除个数)。












2331

被折叠的 条评论
为什么被折叠?



