【C++ STL】set/multiset 容器存储结构及常用成员方法操作

set / multiset 容器

一:set / multiset 容器基本概念

(1)set / multiset 容器基本概念:

set 和 multiset 是 C++ 标准库中的关联容器,它们使用红黑树(Red-Black Tree)作为底层数据结构来存储元素。这些容器中的元素是自动按照一定的规则进行排序的,不允许有重复的元素(std::set),或允许有重复的元素(std::multiset)

红黑树是一种平衡二叉搜索树,具有以下特性:

  1. 每个节点是红色或黑色
  2. 根节点是黑色
  3. 每个叶子节点(NIL 节点)是黑色
  4. 如果一个节点是红色,则其子节点必须是黑色
  5. 从任一节点到其每个叶子节点的路径都包含相同数量的黑色节点

这些特性保证了红黑树的平衡性,从而保证了插入、查找和删除操作的时间复杂度都可以在对数时间内完成。

(2)set / multiset 容器特点:

1. 元素唯一:

set 中的元素都是唯一的,即不允许重复的元素出现在集合中。
multiset 中的元素允许重复,可以插入多个相同的值。这是multiset和set主要的不同之处,其他地方功能和 set 类似。

2. 自动排序(从小到大):

set 中的元素按照升序(默认情况下)或根据用户自定义的比较函数进行排序。每次插入新元素时,集合会自动调整元素的位置,以保持有序性。

3. 插入和查找效率较高:

由于 set 使用红黑树作为底层实现,插入和查找的时间复杂度都是 O(log n),其中 n 是集合中的元素个数。

4. 不支持修改元素:

由于元素是有序的,不允许直接修改集合中的元素。若要修改元素,需要先删除原有元素,然后再插入新的元素

二:set / multiset 模板原型

1. set 容器模板原型:

_EXPORT_STD template <class _Kty, class _Pr = less<_Kty>, class _Alloc = allocator<_Kty>>
class set : public _Tree<_Tset_traits<_Kty, _Pr, _Alloc, false>> {
// ordered red-black tree of key values, unique keys
}

_Tree用来实现红黑树的模板,它内部定义了红黑树的各种操作,包括插入、删除、查找等。所以,实际上 set 和 multiset 都是通过继承 _Tree 并传递不同的类型特征来实现的。
_Kty:这是存储在 set 中的元素类型,也就是键(key)的数据类型。
_Pr:这是比较函数(比较器)的类型,用于确定元素之间的顺序。默认情况下,使用 less<_Kty> 作为比较函数,它用于对键进行排序。
_Alloc:这是分配器的类型,用于分配和释放内存。默认情况下,使用 allocator<_Kty> 作为分配器。
false: 这个参数表示 set 不允许重复的键。这是因为 set 是一个有序集合,每个键只能出现一次。

2. multiset 容器模板原型:

_EXPORT_STD template <class _Kty, class _Pr = less<_Kty>, class _Alloc = allocator<_Kty>>
class multiset : public _Tree<_Tset_traits<_Kty, _Pr, _Alloc, true>>{
// ordered red-black tree of key values, non-unique keys
}

与set容器不同之处在于最后一个参数:
true: 这个参数表示 multiset 允许重复的键。与 set 不同,multiset 可以包含多个相同的键。

三:set 容器成员函数使用示例

注意: 使用时包含头文件< set >头文件可以使用 set / multiset;

(1)set 容器常用成员函数汇总

1. s.begin()  // 起始迭代器,指向第一个元素
2. s.end()    // 结束迭代器,指向最后一个元素的后面一个虚拟位置,用于辅助遍历
3. s.clear()  // 删除set容器中的所有的元素
4. s.empty()  // 判断set容器是否为空
5. s.insert() // 插入一个元素
6. s.erase()  // 删除一个元素
7. s.size()   // 返回当前 set 容器中的元素个数
8. s.swap()   // 交换两个集合
9. s.find()   // 查找 key值元素
10. s.count() // 统计 key值元素,主要在 multiset 容器上使用
11. s.empty() // 判断set容器是否为空  

set/multiset 容器打印输出函数:

// set/multset容器打印输出
void printSet(set<int>& s1){
	for (const auto& num : s1){
		cout << num << " ";
	}
	cout << endl;
}

(2)set 容器的创建操作

代码示例:

void test01()
{
	set<int>s1; // 默认构造
	//插入数据,只有用 insert方式
	for (int i = 10; i >0; i--){
		//源码定义返回pair<iterator, bool> //pair是对组,一对 pair<>
		pair<set<int>::iterator, bool> re = s1.insert(i); 
		if (re.second){
			cout << "插入元素" << i << "成功!" << endl;
		}
		else {
			cout << "插入元素" << i << "失败!" << endl;
		}
	}
	cout << "set插入数据,自动从小到大排序:";
	printSet(s1);
	
	// 使用greater<int>排序方式改为 从大到小
	set<int,greater<int>>s;
	for (int i = 10; i > 0; i--){
		s.insert(i);
	}
	cout << "s: ";
	printSet(s); // 10 9 8 7 6 5 4 3 2 1

	set<int>s2(s1); // 拷贝构造
	cout << "拷贝构造:";
	printSet(s2); // 1 2 3 4 5 6 7 8 9 10
	
	set<int>s4(s1.begin(), s1.end());// 区间拷贝
	cout << "区间赋值:";
	printSet(s4);
	
	set<int>s5;
	cout<<"s1是否为空: " << s1.empty() << endl; //0 不为空
	cout<<"s5是否为空: " << s5.empty() << endl; //1 为空
	
	multiset<int>s3 = { 4,4,5,1,6 }; // multiset 容器可有重复值!
	cout << "mutilset s3初始化:";
	printMultiSet(s3); // 1 4 4 5 6
}

(3)增删查改操作

1. 插入操作

函数原型:

insert(val) :插入单个元素val

代码示例

void test02(){
	set<int>s1; // 默认构造
	//插入数据,只有insert方式
	for (int i = 10; i >0; i--){
		s1.insert(i); 
	}
	printSet(s1);
}	
2. 删除操作

函数原型:

erase(pos):删除迭代器指定位置的元素
erase(elem):删除指定元素
erase(begin,end):删除该区间内的元素
clear():删除set容器中的所有的元素,清空操作

代码示例:

void test03()
{
	set<int>s1; 
	for (int i = 10; i > 0; i--){
		s1.insert(i);
	}
	printSet(s1);// 1 2 3 4 5 6 7 8 9 10
	
	// erase(pos)
	cout << "删除迭代器指向的位置元素:";
	s1.erase(s1.begin());
	printSet(s1); //2 3 4 5 6 7 8 9 10

	// erase(elem)
	cout << "删除指定元素:";
	s1.erase(10);
	printSet(s1);  // 2 3 4 5 6 7 8 9

	// erase(begin,end)
	cout << "区间删除元素:";
	set<int>::iterator it = s1.begin();
	it++;
	s1.erase(it, s1.end()); // 删除 2 - end 
	printSet(s1);

	s1.clear();
	cout <<"s1 元素大小为:" << s1.size() << endl; // 0
}
3. 查找操作

函数原型:

find(key):查找 key 值,如果容器中不存在该元素,返回 s.end() 。

代码示例:

void test04(){
	set<int>s1;
	for (int i = 10; i > 0; i--){
		s1.insert(i);
	}
	printSet(s1);
	//find(key) //查找 key元素
	set<int>::iterator it = s1.find(-1); //查找key成功,返回该键的迭代器,否则,返回set.end()
	if (it != s1.end()){
		cout << "查找到该元素:" << *it << endl;
	}
	else cout << "查找不成功" << endl;
}
4. 修改操作

不能直接修改容器内数据,只能删除某元素再插入要修改的数值。

int main() {
    std::set<int> mySet = {1, 2, 3, 4, 5};
    // 修改元素 3 为 6
    auto it = mySet.find(3);
    if (it != mySet.end()) {
        mySet.erase(it); // 先删除元素 3
        mySet.insert(6); // 再插入元素 6
    }
    for (const auto& num : mySet) {
        std::cout << num << " ";
    } // 输出:1 2 4 5 6
    return 0;
}

(4)set容器统计元素个数

代码示例:

//count()
void test05(){
	set<int>s1;
	for (int i = 10; i > 0; i--){
		s1.insert(i);
	}
	printSet(s1);
	// count(key) 统计key元素数量 对set 0 或者 1;
	// multiset 就是正常情况计数 
	cout << "元素1的数量为:" << s1.count(1) << endl; // 1
}

(5)set 容器交换操作

函数原型:

swap();

代码示例:

void test06() {
	set<int>s1;
	for (int i = 10; i > 0; i--){
		s1.insert(i);
	}
	cout << "s1: ";
	printSet(s1);

	set<int>s4 = { 4,5,6,8,1,3 };

	cout << "s4和s1交换:" << endl;
	s4.swap(s1);
	
}

(6)set 容器修改排序规则

1. 内置数据类型指定排序规则
//从大到小排序 -- 仿函数
class CompareValue{
public:
	bool operator()(const int& v1,const int& v2)const {
		return v1 > v2;
	}
};

void test07(){
	set<int,CompareValue>s1;

	for (int i = 10; i > 0; i--){
		s1.insert(i);
	}
	for (const auto& num : s1){
		cout << num << " " ;
	}
	cout << endl; // 10 9 8 7 6 5 4 3 2 1
}
2. 自定义数据类型指定排序规则
//自定义数据类型
class myInfo {
public:
	myInfo(string name,int age){
		this->Age = age;
		this->Name = name;
	}

	string Name;
	int Age;
};

// 自定义比较函数,实现从大到小排序
struct ascend {
	bool operator()(const myInfo& m1,const myInfo& m2)const {
		return m1.Age > m2.Age; 
	}
};

void test08() {
	set<myInfo,ascend> m;
	myInfo m1("zhan", 10);
	myInfo m2("zhan1", 64);
	myInfo m3("zhan2", 84);
	myInfo m4("zhan3", 53);
	myInfo m5("zhan4", 38);

	m.insert(m1);
	m.insert(m2);
	m.insert(m3);	
	m.insert(m4);
	m.insert(m5);

	for (const auto& num : m) {
		cout << num.Name << " " << num.Age << endl;
	}
}

输出结果:

10 9 8 7 6 5 4 3 2 1
zhan2 84
zhan1 64
zhan3 53
zhan4 38
zhan 10

四:总结

  1. 使用 set 当你需要一个容器存储唯一元素,且不允许重复,默认升序。
  2. 使用 multiset 当你需要一个容器存储元素,允许重复,默认升序排序。

在选择使用 set 还是 multiset 时,根据你的需求来决定是否允许重复元素。
此外还有 unordered_set / multiset,是set 和 multiset的无序版,具体内容可以查看博客内容


希望能更好的掌握 set/multiset 的特性及使用方法。
欢迎关注🔎点赞👍收藏⭐️留言📝

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值