前言
前面我们讲了C语言的基础知识,也了解了一些初阶数据结构,并且讲了有关C++的命名空间的一些知识点以及关于C++的缺省参数、函数重载,引用 和 内联函数也认识了什么是类和对象以及怎么去new一个 ‘对象’ ,也了解了C++中的模版,以及学习了几个STL的结构也相信大家都掌握的不错,接下来博主将会带领大家继续学习有关C++比较重要的知识点—— set & multiset (STL) 。下面话不多说坐稳扶好咱们要开车了😍
一、set简介
在C++中,set
是标准库中的一个容器,它实现了有序、不重复的元素集合。set
容器基于红黑树数据结构实现,提供了一系列的成员函数和操作符,用于对元素进行插入、删除、查找等操作。其中set
又分为set
和multiset
,接下来博主将会带着大家认识一下这两个函数。
二、std::set
1. std::set简介
std::set
是C++标准库中提供的一个容器,它实现了一种有序的、不重复的元素集合。每个元素在std::set
容器中都唯一,并且以特定的顺序进行排序。std::set
是基于红黑树数据结构实现的,因此它具有高效的插入、删除和查找操作。
🍔以下是std::set
容器的一些特点:
- 有序存储:
std::set
中的元素是按照特定的排序准则进行有序存储的,默认以升序排列。可以通过提供自定义的比较函数或函数对象来改变排序方式。 - 唯一性:
std::set
容器中的元素是唯一的,不允许存在重复的元素。当尝试插入已经存在的元素时,插入操作不会生效。 - 动态大小:
std::set
容器可以根据需要动态地增加或减少其大小。可以通过插入和删除操作来修改容器中的元素数量。 - 快速插入、删除和查找:基于红黑树数据结构,
std::set
容器提供了高效的插入、删除和查找操作。这些操作的平均时间复杂度为O(logN),其中N是容器中的元素数。 - 迭代器支持:
std::set
容器提供了迭代器,可以用于遍历容器中的元素,并对其进行读取或修改操作。 - 不支持直接访问元素:由于
std::set
是基于有序集合实现的,它并不支持通过下标操作符([]
)直接访问元素。如果需要按索引访问元素,可以使用其他容器类型,如std::vector
。 - 内存开销相对较大:由于红黑树的特性,
std::set
容器在内存使用方面可能比其他容器类型更占用空间。
🚨🚨注意:
std::set
中的元素是有序存储的,因此插入、删除操作的时间复杂度为O(logN),查找操作的时间复杂度也为O(logN),其中N为std::set
中元素的个数。- 使用
std::set
需要包含<set>
头文件,并使用std::set
关键字声明一个std::set
对象。
2. std::set的使用
- 基本使用
#include <set>
#include <iostream>
int main() {
std::set<int> mySet;
// 插入元素
mySet.insert(10);
mySet.insert(5);
mySet.insert(8);
// 遍历元素
for (const auto& elem : mySet) {
std::cout << elem << " ";
}
// 输出:5 8 10
// 查找元素
auto it = mySet.find(8);
if (it != mySet.end()) {
std::cout << "Element found: " << *it << std::endl;
} else {
std::cout << "Element not found" << std::endl;
}
// 删除元素
mySet.erase(8);
return 0;
}
上述示例演示了创建一个std::set
对象,插入元素并遍历打印,查找特定元素,并删除元素的基本操作。
- std::set的模板参数列表
🍪模板参数列表用于指定容器存储的元素类型和相关的比较函数。std::set
的模板参数列表如下:
template<
class Key,
class Compare = std::less<Key>,
class Allocator = std::allocator<Key>
> class set;
Key
:指定std::set
容器中存储的元素类型。它可以是任何可比较的类型,例如基本数据类型(整数、浮点数)、结构体、类对象等。元素类型必须支持<
或自定义的比较函数用于元素排序。Compare
(可选):用于比较元素的函数或函数对象,用于确定元素的相对顺序。默认情况下,使用std::less<Key>
,即使用<
运算符进行比较。如果希望使用其他比较方式,可以自定义一个比较函数或函数对象,并将其作为Compare
参数传递给std::set
。比较函数或函数对象应该满足严格弱排序的要求。Allocator
(可选):指定用于分配内存的分配器类型。默认情况下,使用std::allocator<Key>
,它使用标准的内存分配操作符new
和delete
。也可以使用自定义的分配器类型,以便对内存分配进行更灵活的控制。
- std::set的构造函数
下面的表格中列出了 std::set
的各种构造函数及其描述:
构造函数 | 描述 |
---|---|
set() | 默认构造函数,创建一个空的 std::set 容器。 |
set(const set& other) | 复制构造函数,创建一个新的 std::set 容器,并复制给定容器中的所有元素。 |
set(set&& other) noexcept | 移动构造函数,创建一个新的 std::set 容器,并使用给定容器中的元素初始化它。 |
set(InputIt first, InputIt last) | 使用迭代器构造一个 std::set 容器,并将给定范围内的元素添加到其中。输入迭代器应指向可比较的类型。 |
set(InputIt first, InputIt last, const Compare& comp) | 使用自定义比较函数构造一个 std::set 容器,并将给定范围内的元素添加到其中。输入迭代器应指向可比较的类型。 |
set(std::initializer_list < key> init) | 使用初始化列表构造一个 std::set 容器。 |
set(std::initializer_list< Key> init, const Compare& comp) | 使用自定义比较函数和初始化列表构造一个 std::set 容器。 |
其中,Key
表示元素类型,Compare
表示比较函数类型,InputIt
表示输入迭代器类型。
这些构造函数提供了多种不同的方式来初始化 std::set
容器,并将元素添加到其中。可以使用默认构造函数创建一个空容器,使用复制构造函数从另一个容器中复制元素,或使用迭代器和初始化列表来初始化容器。您还可以通过在构造函数参数中指定比较函数来控制元素的排序方式。
- std::set的迭代器
下面的表格中列出了 std::set
的一些常用迭代器相关函数及其描述:
函数 | 描述 |
---|---|
iterator begin() | 返回一个指向容器中第一个元素的迭代器。 |
const_iterator begin() const | 返回一个指向容器中第一个元素的只读迭代器。 |
iterator end() | 返回一个指向容器中最后一个元素之后位置的迭代器。 |
const_iterator end() const | 返回一个指向容器中最后一个元素之后位置的只读迭代器。 |
reverse_iterator rbegin() | 返回一个指向容器中最后一个元素的反向迭代器。 |
const_reverse_iterator rbegin() const | 返回一个指向容器中最后一个元素的只读反向迭代器。 |
reverse_iterator rend() | 返回一个指向容器中第一个元素之前位置的反向迭代器。 |
const_reverse_iterator rend() const | 返回一个指向容器中第一个元素之前位置的只读反向迭代器。 |
const_iterator cbegin() const | 返回一个指向容器中第一个元素的只读迭代器。 |
const_iterator cend() const | 返回一个指向容器中最后一个元素之后位置的只读迭代器。 |
const_reverse_iterator crbegin() const | 返回一个指向容器中最后一个元素的只读反向迭代器。 |
const_reverse_iterator crend() const | 返回一个指向容器中第一个元素之前位置的只读反向迭代器。 |
这些函数用于获取不同类型的迭代器,以访问 std::set
容器中的元素。您可以使用普通的正向迭代器(begin()
和 end()
)来遍历容器中的元素,也可以使用反向迭代器(rbegin()
和 rend()
)来从后往前遍历容器。还提供了只读的常量迭代器和只读的常量反向迭代器,以及对应的 cbegin()
、cend()
、crbegin()
和 crend()
函数。
- std::set容量与元素访问函数
⭕容量函数:
bool empty() const noexcept
:- 该函数用于检查容器是否为空。
- 如果容器为空,则返回
true
;否则,返回false
。
size_t size() const noexcept
:- 该函数返回容器中元素的数量。
- 返回一个无符号整数,表示容器中存储的元素个数。
size_t max_size() const noexcept
:- 该函数返回容器能够存储的最大元素数量。
- 返回一个无符号整数,表示容器能够支持的最大元素数量。
⭕元素访问函数:
std::set::iterator find(const Key& key)
:- 如果找到了匹配的元素,则返回指向该元素的迭代器;
- 如果未找到匹配的元素,则返回指向容器结尾的迭代器
end()
。
std::set::const_iterator find(const Key& key) const
:- 该函数在容器中查找给定的键值,并返回指向该元素的只读迭代器。
- 这是
find
函数的只读版本,返回一个指向匹配元素的只读迭代器或end()
。
size_t count(const Key& key) const
:- 该函数统计容器中具有给定键值的元素个数。
- 返回一个无符号整数,表示具有给定键值的元素个数。
std::pair<std::set::iterator, std::set::iterator> equal_range(const Key& key)
:- 该函数返回一个
pair
对象,其中包含容器中所有键值等于给定键值的元素范围的起始和结束迭代器。 - 返回一个由两个迭代器组成的
pair
对象,第一个迭代器指向键值范围的起始位置,第二个迭代器指向键值范围的结束位置后面的位置。
- 该函数返回一个
std::pair<std::set::const_iterator, std::set::const_iterator> equal_range(const Key& key) const
:- 该函数是
equal_range
函数的只读版本,返回一个pair
对象,其中包含容器中所有键值等于给定键值的只读元素范围的起始和结束迭代器。3. set的所有函数(表)
- 该函数是
函数 | 用法 | 功能 |
---|---|---|
bool empty() const noexcept | set.empty() | 检查容器是否为空 |
size_t size() const noexcept | set.size() | 返回容器中元素的数量 |
size_t max_size() const noexcept | set.max_size() | 返回容器支持的最大元素数量 |
std::pair insert(const value_type& value) | set.insert(value) | 向容器中插入一个元素 |
iterator insert(iterator hint, const value_type& value) | set.insert(hint, value) | 在给定位置之前插入一个元素 |
template < class InputIt> void insert(InputIt first, InputIt last) | set.insert(first, last) | 将指定范围内的元素插入到容器中 |
iterator erase(iterator pos) | set.erase(pos) | 删除给定位置的元素 |
size_type erase(const key_type& key) | set.erase(key) | 删除具有给定键值的元素 |
iterator erase(iterator first, iterator last) | set.erase(first, last) | 删除给定范围内的元素 |
void swap(set& other) | set.swap(other) | 交换两个集合容器的内容 |
void clear() noexcept | set.clear() | 清空容器中的所有元素 |
iterator find(const key_type& key) | set.find(key) | 在容器中查找具有给定键值的元素,并返回指向该元素的迭代器;如果未找到,则返回 end() 迭代器 |
const_iterator find(const key_type& key) const | set.find(key) | 在容器中查找具有给定键值的元素,并返回指向该元素的只读迭代器;如果未找到,则返回 end() 迭代器 |
size_type count(const key_type& key) const | set.count(key) | 返回具有给定键值的元素在容器中的个数 |
iterator lower_bound(const key_type& key) | set.lower_bound(key) | 返回第一个不小于给定键值的元素的迭代器 |
const_iterator lower_bound(const key_type& key) const | set.lower_bound(key) | 返回第一个不小于给定键值的元素的只读迭代器 |
iterator upper_bound(const key_type& key) | set.upper_bound(key) | 返回第一个大于给定键值的元素的迭代器 |
const_iterator upper_bound(const key_type& key) const | set.upper_bound(key) | 返回第一个大于给定键值的元素的只读迭代器 |
std::pair equal_range(const key_type& key) | set.equal_range(key) | 返回一个 pair 对象,其中包含容器中所有键值等于给定键值的元素范围的起始和结束迭代器 |
std::pair equal_range(const key_type& key) const | set.equal_range(key) | 返回一个 pair 对象,其中包含容器中所有键值等于给定键值的只读元素范围的起始和结束迭代器 |
key_compare key_comp() const | set.key_comp() | 返回用于键值比较的函数对象 |
value_compare value_comp() const | set.value_comp() | 返回用于值比较的函数对象 |
iterator begin() noexcept | set.begin() | 返回指向容器中第一个元素的迭代器 |
const_iterator begin() const noexcept | set.begin() | 返回指向容器中第一个元素的只读迭代器 |
iterator end() noexcept | set.end() | 返回指向容器中最后一个元素之后位置的迭代器 |
const_iterator end() const noexcept | set.end() | 返回指向容器中最后一个元素之后位置的只读迭代器 |
reverse_iterator rbegin() noexcept | set.rbegin() | 返回一个指向容器最后一个元素的逆序迭代器 |
const_reverse_iterator rbegin() const noexcept | set.rbegin() | 返回一个指向容器最后一个元素的只读逆序迭代器 |
reverse_iterator rend() noexcept | set.rend() | 返回一个指向容器第一个元素之前位置的逆序迭代器 |
const_reverse_iterator rend() const noexcept | set.rend() | 返回一个指向容器第一个元素之前位置的只读逆序迭代器 |
const_iterator cbegin() const noexcept | set.cbegin() | 返回指向容器中第一个元素的只读迭代器(同 begin() ) |
const_iterator cend() const noexcept | set.cend() | 返回指向容器中最后一个元素之后位置的只读迭代器(同 end() ) |
const_reverse_iterator crbegin() const noexcept | set.crbegin() | 返回一个指向容器最后一个元素的只读逆序迭代器(同 rbegin() ) |
const_reverse_iterator crend() const noexcept | set.crend() | 返回一个指向容器第一个元素之前位置的只读逆序迭代器(同 rend() ) |
三、std::multiset
1. std::multiset简介
std::multiset
是 C++ 标准库中的一个容器,它实现了一个有序的、可重复的元素集合。和 std::set
不同的是,std::multiset
允许存储多个相同的元素。
std::multiset
的特点包括:
- 元素按照升序进行排序,并且插入新元素后仍然保持有序。
- 可以存储相同的元素,即允许重复元素存在。
- 插入和删除操作都具有对数复杂度。
- 支持快速查找、遍历和迭代操作。
与其他容器类似,std::multiset
提供了一系列成员函数来方便地操作集合数据,例如插入元素、删除元素、查找元素等。此外,std::multiset
还提供了迭代器来遍历集合中的元素,并且可以使用自定义的比较函数来指定元素的排序方式。
以下是 std::multiset
常用的一些成员函数:
insert
: 插入一个元素或者一个范围的元素到multiset
中。erase
: 删除一个元素或者一个范围的元素。find
: 在multiset
中查找指定元素,并返回一个指向该元素的迭代器。count
: 计算某个元素在multiset
中的个数。size
: 返回元素个数。empty
: 检查multiset
是否为空。clear
: 清空multiset
中的所有元素。begin
和end
: 返回指向第一个元素和最后一个元素之后位置的迭代器,用于遍历multiset
中的元素。
🚨🚨注意:std::multiset
并不支持修改元素的值,因为它保持元素有序的特性。如果需要修改元素的值,可以先从容器中删除该元素,然后再插入修改后的值。
下面是一个简单的示例代码,展示了如何使用 std::multiset
:
#include <iostream>
#include <set>
int main() {
std::multiset<int> numbers;
// 插入元素
numbers.insert(5);
numbers.insert(2);
numbers.insert(7);
numbers.insert(5); // 允许插入重复元素
// 遍历输出元素
for (const auto& num : numbers) {
std::cout << num << " ";
}
std::cout << std::endl;
// 查找元素
auto it = numbers.find(5);
if (it != numbers.end()) {
std::cout << "找到元素 " << *it << std::endl;
}
// 删除元素
numbers.erase(2);
// 输出剩余的元素数量
std::cout << "剩余元素个数:" << numbers.size() << std::endl;
return 0;
}
输出结果:
2 5 5 7
找到元素 5
剩余元素个数:3
🔴在使用上面与std::map的使用方法以及函数类型都一样,这里我就不再过多的赘述了。(详细的说明可以看官方文档的介绍)
四、std::set与std::multiset的比较
⭕相同点
- 容器类型:它们都是关联容器,用于存储一组按照特定顺序排列的元素。
- 元素的访问:它们提供了类似于容器的访问方式,可以使用迭代器进行遍历,还可以直接访问指定位置的元素。
- 迭代器支持:它们都支持正向迭代器,可以用于遍历元素。
⭕不同点
元素唯一性:
std::set
中每个元素都是唯一的,容器中不允许出现重复的元素。std::multiset
允许存储多个相同的元素,可以在容器中存储重复的元素。
排序:
std::set
中的元素是按照升序进行排序的,默认使用<
运算符进行比较。std::multiset
也对元素进行排序,但与std::set
不同的是,它允许存储相同的元素,因此可以有多个相同的元素按照顺序存储。
插入操作:
- 向
std::set
插入重复的元素将被忽略,只有第一个插入的元素会被保留。 - 向
std::multiset
插入重复的元素会将所有的元素都保留。
- 向
查找操作:
std::set
提供的查找函数返回的迭代器指向找到的元素或者尾后迭代器(如果未找到)。std::multiset
提供的查找函数返回的迭代器指向第一个匹配的元素。
删除操作:
- 从
std::set
中删除一个元素将删除所有与之相等的元素。 - 从
std::multiset
中删除一个元素只会删除一个匹配的元素。
- 从
如何选择set 和 multiset
- 如果需要保持元素的唯一性并进行快速查找,可以选择
std::set
。 - 如果需要允许重复的元素,并按照顺序进行存储和查找,可以选择
std::multiset
。温馨提示
感谢您对博主文章的关注与支持!另外,我计划在未来的更新中持续探讨与本文相关的内容,会为您带来更多关于C++以及编程技术问题的深入解析、应用案例和趣味玩法等。请继续关注博主的更新,不要错过任何精彩内容!
再次感谢您的支持和关注。期待与您建立更紧密的互动,共同探索C++、算法和编程的奥秘。祝您生活愉快,排便顺畅!中秋快乐!