C++ | STL 单集合容器set和多集合容器multiset

2024-03-31 19:18
文章标签 c++ set 容器 集合 stl multiset

本文主要是介绍C++ | STL 单集合容器set和多集合容器multiset,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

一.单集合容器set和多集合容器multiset

二.set常用的构造方式

三.set的插入,删除以及查找操作

四.set的访问函数

五.比较 set 和multiset的不同

六.使用set和multiset时的注意事项


一.单集合容器set和多集合容器multiset

set指的是单集合容器,所谓的单集合容器指的是容器里面的数据不能重复,所需要的头文件为#include<set>,其底层为红黑树

multiset指的是多集合容器,所谓的多集合容器指的是容器里面的数据可以重复,所需要的头文件为#include<set>,其底层为红黑树

二.set常用的构造方式

#include<iostream>
#include<set>
#include<algorithm>template<typename Container>
void Show(Container con)
{typename Container::iterator it = con.begin();while (it != con.end()){std::cout << *it << " ";it++;}std::cout << std::endl;
}int main()
{std::set<int> myset; //默认的构造 int arr[] = { 13, 2, 343, 2, 53, 6 };int len = sizeof(arr) / sizeof(arr[0]);std::set<int> myset1(arr, arr + len);Show(myset);Show(myset1);return 0;
}

在构造set容器myset时,调用的是set类中默认的构造函数,(所有的容器中都有默认的构造函数),且构造的同时没有做任何事情。

在构造set容器myset1时,我们传入的参数是一个迭代器区间,即将arr数组的开始和末尾的后一个位置传入,也就是将arr中的数据插入到myset1中。并且我们发现,虽然在向myset1中插入元素时,有重复的元素2,但最终的打印的结果却只有一个2,这是因为单集合容器set里面的数据不能重复。而且打印的结果是从小到大打印的,这是因为单集合容器set的底层为红黑树,默认的排序方式是从小到大的。

三.set的插入,删除以及查找操作

#include<iostream>
#include<set>
#include<algorithm>template<typename Container>
void Show(Container con)
{typename Container::iterator it = con.begin();while (it != con.end()){std::cout << *it << " ";it++;}std::cout << std::endl;
}int main()
{std::set<int> myset; //默认的构造 std::set<int> myset1;int arr[] = { 11,22,33,44 };int len = sizeof(arr) / sizeof(arr[0]);myset1.insert(arr, arr + len);Show(myset1);for (int i = 0; i < 5; i++){myset.insert(i + 1);//按位置插入   set只给出了数据 没有给出位置。}myset.insert(myset.begin(), 10);//位置无效 Show(myset);myset.erase(10);std::set<int>::iterator it = myset.begin();myset.erase(it);Show(myset);return 0;
}

我们知道,set容器的底层是红黑树,而红黑树是在二叉排序树的基础上实现的,这也就意味着在向set容器中插入元素时不需要传入位置,因为红黑树会自动根据插入节点的值的大小来调整该节点的位置,使得最后该二叉树还是一个红黑树,因此也就不存在头插,尾插以及按位置插等方法,例如上述代码中,哪怕你在insert函数中给定了要插入的位置,系统也会将该位置忽略,所以我们在调用insert时只需要给定要插入的值就可以。或者我们也可以给定一个迭代器区间,将这个区间内的所有值都插入。

insert的重载还有很多,在这里就不一一列举。

因为set的底层为红黑树,因此也不存在头删,尾删,因为每个节点的位置随时都可能发生变动

erase的重载还有很多,在这里就不一一列举。

关于在set容器中查找一个值为val的元素是否存在,在std域下有一个泛型算法find可以实现,这个find函数的形参是要查找的范围即一个迭代器区间,以及要查找的值val,但这个find的时间复杂度为O(n),因为这个泛型算法采用的是顺序遍历。另外,在set类中也会提供一个find函数,这个find函数的形参为要查找的值val,而他的时间复杂度为O(log2 n),因为set底层是红黑树,而红黑树中的数据已经经过了排列而变得有序了,那么就可以使用二分法来查找

因此set容器的优点就是“快速查找”

#include<iostream>
#include<set>
#include<algorithm>template<typename Container>
void Show(Container con)
{typename Container::iterator it = con.begin();while (it != con.end()){std::cout << *it << " ";it++;}std::cout << std::endl;
}int main()
{int arr[] = { 13, 2, 343, 2, 53, 6 };int len = sizeof(arr) / sizeof(arr[0]);std::set<int> myset(arr, arr + len);Show(myset);//find  时间复杂度 O(n)  泛型算法库std::set<int>::iterator fit1 = std::find(myset.begin(),myset.end(), 53);if (fit1 != myset.end()){std::cout << *fit1 << std::endl;}//find	时间复杂度O(log2 n)		set类中提供  优点std::set<int>::iterator fit2 = myset.find(53);if (fit2 != myset.end()){std::cout << *fit2 << std::endl;}return 0;
}

四.set的访问函数

#include<iostream>
#include<set>
#include<algorithm>template<typename Container>
void Show(Container con)
{typename Container::iterator it = con.begin();while (it != con.end()){std::cout << *it << " ";it++;}std::cout << std::endl;
}int main()
{int arr[] = { 1,9,8,10,7,3,6,2,5,4 };int len = sizeof(arr) / sizeof(arr[0]);std::set<int> myset(arr, arr + len);Show(myset);return 0;
}

我们发现我们在访问某一set容器时,最终的结果是从小到大排序的,因为这是系统默认的,我们也可以通过添加参数,来决定底层的二叉树对数据的排序方法。例如:

#include<iostream>
#include<set>
#include<algorithm>template<typename Container>
void Show(Container con)
{typename Container::iterator it = con.begin();while (it != con.end()){std::cout << *it << " ";it++;}std::cout << std::endl;
}int main()
{int arr[] = { 1,9,8,10,7,3,6,2,5,4 };int len = sizeof(arr) / sizeof(arr[0]);std::set<int,std::less<int>> myset(arr, arr + len);std::set<int, std::greater<int>> myset1(arr, arr + len);Show(myset);Show(myset1);return 0;
}

五.比较 set 和multiset的不同

所谓的multiset其实就是多集合容器,其底层也是一棵红黑树,之前我们讲过,单集合容器set中不允许有重复的数据存在,而多集合容器multiset恰恰与其相反,因为它允许重复的元素存在。多集合容器multiaet所需要的头文件也为#include<set>。下面,我们来举一个例子,来看一下二者的区别。

#include<iostream>
#include<set>
#include<iterator>
#include<algorithm>int main()
{const int size = 16;int a[size] = { 17,11,29,89,73,53,61,37,41,29,3,47,31,59,5,2 };std::multiset<int> intMultiset(a, a + size);	//用a来初始化INTMS容器实例std::ostream_iterator<int> output(std::cout, " ");//整型输出迭代子output,可通过cout输出用空格分隔的整数std::cout << "这里原来有" << intMultiset.count(17)<< "个数值17" << std::endl; intMultiset.insert(17); //插入一个重复的数17std::cout << "输入后这里有" << intMultiset.count(17) << "个数值17" << std::endl;std::multiset<int>::const_iterator result;result = intMultiset.find(18);//找到则返回所在位置,设找到返回与调end()返回的同样值if (result == intMultiset.end()){std::cout << "没找到值18" << std::endl;}else{std::cout << "找到值18" << std::endl;}std::cout << "intMultiset容器中有" << std::endl;copy(intMultiset.begin(), intMultiset.end(), output);//输出容器中全部元素std::cout << std::endl;
}

我们看到一开始多集合容器multiset中只有一个17,但在我们又插入了一个17后,多集合容器multiset中17的个数就变为2个了,所以多集合容器multiset中允许数据重复。因为多集合容器multiset的用法几乎与set相同,所以我们不在这里一一阐述,至于上述代码中出现的泛型算法copy和输出流迭代器在我以前的博客 迭代器 那一篇有讲到过,感兴趣的读者可以自行去学习。

六.使用set和multiset时的注意事项

我们知道set的底层是红黑树,每次我们插入一个新的数据后,红黑树都要按中序遍历的形式从小到大(默认是从小到大)的对数据进行排序,一般我们我们插入的数据的类型都是内置类型,例如 int char double ......,但是如果我们要插入的数据的类型是自定义类型时,红黑树就不知道该怎么对这些自定义类型的数据排序了。所以当我们向set中插入自定义类型的数据时,那么该自定义类型的类中就要提供比较运算符的重载函数,便于set进行排序。multiset的注意事项与set相同。

 

这篇关于C++ | STL 单集合容器set和多集合容器multiset的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/865109

相关文章

使用C++实现链表元素的反转

《使用C++实现链表元素的反转》反转链表是链表操作中一个经典的问题,也是面试中常见的考题,本文将从思路到实现一步步地讲解如何实现链表的反转,帮助初学者理解这一操作,我们将使用C++代码演示具体实现,同... 目录问题定义思路分析代码实现带头节点的链表代码讲解其他实现方式时间和空间复杂度分析总结问题定义给定

C++初始化数组的几种常见方法(简单易懂)

《C++初始化数组的几种常见方法(简单易懂)》本文介绍了C++中数组的初始化方法,包括一维数组和二维数组的初始化,以及用new动态初始化数组,在C++11及以上版本中,还提供了使用std::array... 目录1、初始化一维数组1.1、使用列表初始化(推荐方式)1.2、初始化部分列表1.3、使用std::

C++ Primer 多维数组的使用

《C++Primer多维数组的使用》本文主要介绍了多维数组在C++语言中的定义、初始化、下标引用以及使用范围for语句处理多维数组的方法,具有一定的参考价值,感兴趣的可以了解一下... 目录多维数组多维数组的初始化多维数组的下标引用使用范围for语句处理多维数组指针和多维数组多维数组严格来说,C++语言没

Go语言中三种容器类型的数据结构详解

《Go语言中三种容器类型的数据结构详解》在Go语言中,有三种主要的容器类型用于存储和操作集合数据:本文主要介绍三者的使用与区别,感兴趣的小伙伴可以跟随小编一起学习一下... 目录基本概念1. 数组(Array)2. 切片(Slice)3. 映射(Map)对比总结注意事项基本概念在 Go 语言中,有三种主要

c++中std::placeholders的使用方法

《c++中std::placeholders的使用方法》std::placeholders是C++标准库中的一个工具,用于在函数对象绑定时创建占位符,本文就来详细的介绍一下,具有一定的参考价值,感兴... 目录1. 基本概念2. 使用场景3. 示例示例 1:部分参数绑定示例 2:参数重排序4. 注意事项5.

使用C++将处理后的信号保存为PNG和TIFF格式

《使用C++将处理后的信号保存为PNG和TIFF格式》在信号处理领域,我们常常需要将处理结果以图像的形式保存下来,方便后续分析和展示,C++提供了多种库来处理图像数据,本文将介绍如何使用stb_ima... 目录1. PNG格式保存使用stb_imagephp_write库1.1 安装和包含库1.2 代码解

C++实现封装的顺序表的操作与实践

《C++实现封装的顺序表的操作与实践》在程序设计中,顺序表是一种常见的线性数据结构,通常用于存储具有固定顺序的元素,与链表不同,顺序表中的元素是连续存储的,因此访问速度较快,但插入和删除操作的效率可能... 目录一、顺序表的基本概念二、顺序表类的设计1. 顺序表类的成员变量2. 构造函数和析构函数三、顺序表

使用C++实现单链表的操作与实践

《使用C++实现单链表的操作与实践》在程序设计中,链表是一种常见的数据结构,特别是在动态数据管理、频繁插入和删除元素的场景中,链表相比于数组,具有更高的灵活性和高效性,尤其是在需要频繁修改数据结构的应... 目录一、单链表的基本概念二、单链表类的设计1. 节点的定义2. 链表的类定义三、单链表的操作实现四、

C#比较两个List集合内容是否相同的几种方法

《C#比较两个List集合内容是否相同的几种方法》本文详细介绍了在C#中比较两个List集合内容是否相同的方法,包括非自定义类和自定义类的元素比较,对于非自定义类,可以使用SequenceEqual、... 目录 一、非自定义类的元素比较1. 使用 SequenceEqual 方法(顺序和内容都相等)2.

使用C/C++调用libcurl调试消息的方式

《使用C/C++调用libcurl调试消息的方式》在使用C/C++调用libcurl进行HTTP请求时,有时我们需要查看请求的/应答消息的内容(包括请求头和请求体)以方便调试,libcurl提供了多种... 目录1. libcurl 调试工具简介2. 输出请求消息使用 CURLOPT_VERBOSE使用 C