【C++21天养成计划】集合——快速上手STL set (multiset) 类(Day16)

大家好!我是【AI 菌】,一枚不熬夜的程序员。我热爱AI、热爱分享、热爱开源! 这博客是我对学习的一点总结与思考。如果您也对 深度学习、机器视觉、算法、Python、C++ 感兴趣,可以关注我的动态,我们一起学习,一起进步~


在这里插入图片描述


一、STL 集合类

1.1 简介

在学习STL 集合类之前,我们先回顾一下数学中集合的概念。集合中的元素具有互异性无序性。互异性是指集合中的元素各不相同,无序性是指元素与所在位置无关。
在这里插入图片描述在C++中,为了方便各种数据操作和处理。在传统集合的概念上进行类一定的改造,将其封装为了STL 集合类。STL 集合类包含set、multiset、unordered_set 和 unordered_multiset。下面来分别介绍一下这4种集合类的区别:

  • set。存于集合里的元素经过排序,且每个元素是唯一的
  • multiset。存于集合里的元素经过排序,且每个元素可以不唯一
  • unordered_set。存于集合里的元素未经排序,且每个元素是唯一的。
  • unordered_multiset。存于集合里的元素未经排序,且每个元素可以不唯一。

总的来说, set 和 multiset 用于存储一组经过排序的元素,其查找元素的复杂度为O(log n),而 unordered_set 和 unordered_multiset 中的元素是未经排序的,所以集合中元素的插入和查找时间是固定的。

set 和 multiset的区别在于,set 只能存储唯一的值,而 multiset 可以存储重复的值。

提示:要使用 set 和 multiset 类,需要包含头文件:# include < set >

1.2 集合类的优缺点

对于需要频繁查找的应用程序来说,STL set 和 multiset 很有优势,因为其元素是经过排序的,因此查找的速度会更快

这种优势的实现方式是,在向集合中插入元素时通过find(),找到合适的位置进行排序。因此,在新增(插入)元素时,无疑会增加额外的开销。好在是find()内部实现使用的是二叉树结构。该结构用于查找的时间复杂度往往在O(log n)左右。

缺点在于,set 和 multiset 不能像顺序容器(比如 vector)一样,可以使用其他元素替换给定位置的元素。这是因为 set 和 multiset 将把新元素同二叉树中的其他元素进行比较,进而将其放在其他位置。

总的来说,与顺序容器(比如 vector)相比:

  • 优点是:set 和 multiset 内部由类似二叉树的结构实现,所以查找速度快。
  • 缺点是:在新增元素时,会增加一点额外的开销;同时,不能使用其他元素直接替换给定位置的元素。
    在这里插入图片描述

1.3 集合类的基本操作

STL set 和 multiset 都是模板类,要使用其成员函数,必须先实例化

实例化 set 和 multiset 通常有以下三种方法:
(1)实例化一个特定类型的 set 或 multiset ,比如实例化一个 int 类型的集合。

set <int> setNums;
multiset <int> msetNUms;

(2)要声明一个包含Tuna对象的 set 和 multiset 。

set <Tuna> setNums;
multiset <Tuna> msetNUms;

(3)通过复制,实例化一个 set 或 multiset 。

set<int>setNums1(setNums); //把setNums赋给setNums1
multiset <int> msetNUms2(msetNUms);

以上三种方式,都没有指定排序标准。因此使用的都是默认谓词less,即升序排序。如果想指定排序的规则,需要提前声明:

# 升序排序
set<int, less<int>>setNums;
# 降序排序
set<int, greater<int>>setNums;

二、集合类常用的成员函数

常用的集合类成员函数包括:插入、查找、删除 等。使用这些成员函数,能够轻松的解决很多集合类数据操作问题,并且使你的代码变得更加简洁可读。下面就来分别来讲一讲这些常用的成员函数!

2.1 插入元素insert()

要在 set 和 multiset 中插入元素,可以使用成员函数 insert(),这个函数接受要插入的值。

#include<iostream>
#include<set>

using namespace std;

template <typename T>
void DisplayContents(const T& Input)
{
    for(auto  iElement = Input.cbegin();iElement != Input.cend(); ++iElement)
        cout<<*iElement<<" ";
    cout<<endl;
}

int main()
{
     set <int> setIntegers;
     multiset <int> msetIntegers;

     setIntegers.insert(60);
     setIntegers.insert(-1);
     setIntegers.insert(300);

     cout<<"显示内容到屏幕:"<<endl;
     DisplayContents(setIntegers);

     msetIntegers.insert(setIntegers.begin(), setIntegers.end());
     msetIntegers.insert(300);

     cout<<"再插入一个元素后,显示内容到屏幕:"<<endl;
     DisplayContents(msetIntegers);

     cout<<"300的数量是:"<<msetIntegers.count(300)<<endl;
     return 0;
}

运行结果:

在这里插入图片描述

2.2 查找元素find()

在 set 和 multiset 中,可以使用成员函数find()来查找相应的元素。multiset 可能包含多个相同的元素,因此对于 multiset,这个函数查找第一个与给定值匹配的元素。

#include<set>
#include<iostream>
using namespace std;

int main()
{
    set<int> setNums;

    //插入元素
    setNums.insert(50);
    setNums.insert(-48);
    setNums.insert(6);
    setNums.insert(160);

    //显示集合元素
    for (auto iElement=setNums.cbegin(); iElement != setNums.cend(); ++iElement)
        cout<<*iElement<<endl;

    //查找集合元素-1
    auto iElementFound = setNums.find(-1);
    if(iElementFound != setNums.end())
        cout<<"Element "<<*iElementFound<<" found!"<<endl;
    else
        cout<<"Element -1 not found in setNums!"<<endl;

    //查找集合元素6
    auto anotherFind = setNums.find(6);
    if(anotherFind != setNums.end())
        cout<<"Element "<<*anotherFind<<" found!"<<endl;
    else
        cout<<"Element 6 not found in setNums!"<<endl;

    return 0;
}

运行结果:
在这里插入图片描述

2.3 删除元素erase()

和大多数关联容器一样,当你想删除 set 和 multiset 中的元素时,可以使用成员函数erase()。

#include<iostream>
#include<set>
using namespace std;

template <typename T>
void DisplayContents(const T& Input)
{
    for(auto  iElement = Input.cbegin();iElement != Input.cend(); ++iElement)
        cout<<*iElement<<" ";
    cout<<endl;
}


int main()
{
    multiset <int, greater<int>> msetNums;
    msetNums.insert(46);
    msetNums.insert(78);
    msetNums.insert(-100);
    msetNums.insert(-100);
    msetNums.insert(8);

    cout<<"msetNums 包含 "<<msetNums.size()<<" 个元素"<<endl;
    cout<<"降序显示mseNums全部元素:"<<endl;
    DisplayContents(msetNums);

    msetNums.erase(-100);

    cout<<"删除-100后,显示mseNums全部元素:"<<endl;
    DisplayContents(msetNums);

    return 0;
}

运行结果:
在这里插入图片描述

三、总结与归纳

3.1 牢记知识点

  • STL set 和 multiset 容器针对频繁查找的情形进行了优化。
  • multiset可存储多个值相同的元素,而set只能存储不同的值。
  • 使用count()可获得元素包含特点值的个数。
  • 使用size()可获得集合包含的元素个数。
  • 在需要频繁插入而很少查找的情形下,不要使用set 和 multiset;在这种情况下,使用 vector 和 list 更合适。

3.2 问与答

问:如何声明一个其元素按降序排序的整型 set?

答:默认情况下使用升序,要声明一个其元素按降序排序的整型 set,需提前声明:

set<int, greater<int>>;

问:如果在一个字符串 set 中插入字符串 AI jun 两次,将会发生神码情况?

答:set 不能存储多个相同的值,因此 set 类的实现不允许插入第二个 AI Jun。如果一定要插入两次,可以使用multiset。

问:我使用函数 find 在 set 中找到了一个元素,并有一个指向该元素的迭代器。能否使用这个迭代器来修改它指向的元素的值。

答:不能,这是因为 set 会根据元素的值大小进行排序,因此不能将迭代器指向任一元素的值。应将指向 set 中的元素的迭代器视为 const 迭代器。

在这里插入图片描述

养成习惯,先赞后看!你的支持是我创作的最大动力!

猜你喜欢

转载自blog.csdn.net/wjinjie/article/details/108444926
今日推荐