【C++】关联式容器——mapset的使用

news2025/1/24 18:01:16

文章目录

  • 1.关联式容器和键值对
    • 1. 关联式容器
    • 2. 键值对
  • 2. 树形结构的关联式容器——set
    • 1. 模版参数列表
    • 2. 默认成员函数
    • 3. 迭代器
    • 4.容量相关操作
    • 5.modify
    • 6.其他操作接口
  • 3. 树形结构的关联式容器——map
    • 1. 默认成员函数
    • 2. 迭代器
    • 3. 容量与数据访问
    • 4.数据修改
    • 5. 其他操作接口

1.关联式容器和键值对

1. 关联式容器

在之前的学习中,我们学习了vector,list,deque等容器,这些容器被统称为序列式容器,因为其底层是线性的数据结构,内部存放的是元素本身,那么关联式容器是什么呢?关联式容器也是用来存放数据的,只是关联式容器中存放数据是以<key,value>键值对的方式存储的,在数据检索的时候效率更高

其实也可以理解成使用了更高级的数据结构,除了单纯的存储数据外,加上了一些特殊的存储逻辑。比如map和set就使用了二叉搜索树这种高级的数据结构。

STL中一共实现了两种不同类型的关联式容器:树形结构哈希结构

其中树形结构的容器有:map、multimap、set、multiset四个。其底层都是使用了**平衡搜索树(红黑树)**实现的,容器中的元素是一个有序的序列。

2. 键值对

用来表示具有一一对应关系的一种结构,该结构中一般只包含两个成员变量keyvaluekey代表键值,value表示与key对应的信息。

现在要建立一个英汉互译的字典,那该字典中必然有英文单词与其对应的中文含义,而且,英文单词与其中文含义是一一对应的关系,即通过该应该单词,在词典中就可以找到与其对应的中文含义。

在SGI- STL中对于键值对pair的定义如下

template <class T1, class T2>
struct pair
{
	typedef T1 first_type;
	typedef T2 second_type;
	T1 first;   //key
	T2 second;  //value
	pair() : first(T1()), second(T2())  //默认构造
	{}
	pair(const T1& a, const T2& b) : first(a), second(b) //构造函数
	{}
};

其中一般使用first表示key,second表示value。

make_pair

按照一般的调用方式,如果我们想创建一个pair的匿对象,需要使用以下代码

pair<string,string>("apple","苹果");

写了这么长一串就是为了创建一个匿名的pair对象多少是有点麻烦的,所以干脆就封装出一个函数模版make_pair,用于快速创建出一个pair对象,使用方式

make_pair("apple","苹果");//调用此函数就返回一个pair对象

在底层,这个函数是这样实现的

template<class T1, class T2>
pair<T1,T2> make_pair(T1 first, T2 second)
{
  	return (pair<T1,T2>(first,second));//本质上还是调用了pair的构造函数
}

2. 树形结构的关联式容器——set

首先还是老样子,挂一下set的使用文档

image-20230512185722599

上述的英文看起来可能对某些同学很吃力,所以我们总结一下

  1. set是按照特定顺序存储唯一元素的容器,里面不会出现重复的内容(可以用来去重
  2. set虽然是采用了键值对的方式存储数据,但是在set中,key==value,可以对照二叉搜索树中的K模型来看。所以在插入数据时只需要插入value即可,不需要构造键值对。
  3. set中的元素一般是const修饰的,不能被修改,但是可以插入或者删除元素
  4. set中的数据的排序方式是可以自己传入的,默认使用的是仿函数less<T>。
  5. set通常比unordered_set更慢,通过其key访问单个元素,但允许根据其顺序对子集进行直接迭代。
  6. set中查找某个元素的时间复杂度为log2n

1. 模版参数列表

接下来我们来看看set的模版参数列表

image-20230513093220748

第一个参数是其内部存放元素类型,实际在其内部存放的是<value,value>键值对;第二个参数看到是Compare就知道是仿函数啦,这是用来决定set中数据的排序方式的,默认按照小于来排序;Alloc是set中元素空间的管理方式,默认使用STL提供的空间配置器。

2. 默认成员函数

1. 构造函数

image-20230513095109454

set的构造方式就非常简洁,一个默认构造,一个迭代器区间构造,还有一个拷贝构造

void Test1()
{
    set<int> s1;//默认构造
    vector<int> v = {1,3,5,2,6,3,5,1};
    set<int> s2(v.begin(), v.end());//迭代器区间构造
    set<int> s3(s2);//拷贝构造
    cout << "s1:";
    for(auto e : s1)
    {
        cout << e << " ";
    }
    cout << endl;
    cout << "s2:";
    for(auto e : s2)
    {
        cout << e << " ";
    }
    cout << endl;
    cout << "s3:";
    for(auto e : s3)
    {
        cout << e << " ";
    }
    cout << endl;
}

image-20230513100128756

2. 析构函数和赋值重载

很显然,在构造set对象的时候有资源申请,所以在析构函数的时候,需要进行资源释放。

析构函数和赋值重载的使用与其他容器非常相似,这里就不赘述了。

3. 迭代器

image-20230513100951008

set迭代器的使用与其他容器的迭代器一样

void Test2()
{
    vector<int> v = {1,3,5,2,6,3,5,1};
    set<int> s(v.begin(), v.end());//使用set存放之后可以去重
    set<int>::iterator it1 = s.begin();
    cout << "正向迭代器:";
    while(it1 != s.end())
    {
        cout <<*it1 << " ";
        ++it1;
    }
    cout << endl;
    set<int>::reverse_iterator it2 = s.rbegin();
    cout << "反向迭代器:";
    while (it2 != s.rend())
    {
        cout << *it2 << " ";
        ++it2;
    }
    cout << endl;
}

image-20230513101908799

4.容量相关操作

函数原型说明
bool empty() const;判断set是否为空,如果为空返回true,否则返回flase
size_t size() const;返回容器的数据个数,类型为size_t(unsigned int)
size_t max_size() const;返回容器中最多能够存放的数据个数

5.modify

1.insert

向set中插入一个对象,并且能够保持结构不变,依然符合平衡搜索树

image-20230513121025994

这里注意一下:第一个插入返回的是一个pair类型的对象,其中pair的第一个成员是一个迭代器,其指向的是插入的元素在set中的位置,第二个成员是bool类型,如果成功插入了数据就返回true,若set中已经存在这个数据,就会插入失败,其中的值就是false。

所以这里insert能够做到两个功能:查找插入

2.erase

在set中删除一个数据,并且结构保持不变

image-20230513121113366

void Test3()
{
    vector<int> v = {1,3,5,2,6,3,5,1};
    set<int> s(v.begin(), v.end());//使用set存放之后可以去重
    cout << "before insert:";
    auto it1 = s.begin();
    while(it1 != s.end())
    {
        cout <<*it1 << " ";
        ++it1;
    }
    cout << endl;
    //insert
    auto a1 = s.insert(4);
    auto a2 = s.insert(4);
    cout << "after insert:";
    it1 = s.begin();
    while(it1 != s.end())
    {
        cout <<*it1 << " ";
        ++it1;
    }
    cout << endl;
    cout << "a1:" << *(a1.first) << ":" << a1.second << endl;
    cout << "a2:" << *(a2.first) << ":" << a2.second << endl;
    cout << "---------------------------" << endl;
    s.erase(4);
    cout << "after erase";
    it1 = s.begin();
    while(it1 != s.end())
    {
        cout <<*it1 << " ";
        ++it1;
    }
    cout << endl;
}
image-20230513123204561

3.swap

交换两个set对象,效率比算法库里实现的高

image-20230513121419860

4.clear

清除set对象中所有数据,但是不删除set对象

image-20230513121444821

6.其他操作接口

1.find

查找set中某个元素,并返回迭代器,如果不存在,就返回end()。

image-20230513123541264

2.lower_bound

以传入的值为下限,返回一个迭代器

image-20230513123800339

3.upper_bound

以传入的值为上限,返回一个迭代器

image-20230513123917369

void Test4()
{
    set<int> s;
    for(int i = 1; i < 10; ++i)
        s.insert(10 * i);
    auto it = s.find(30);
    if(it != s.end())
        s.erase(it);
    for(const auto& e : s)
    {
        cout << e << " ";
    }
    cout << endl;
    auto it1 = s.lower_bound(40);
    auto it2 = s.upper_bound(70);//注意,这里返回的是70的下一个位置
    s.erase(it1,it2);//这里的删除的迭代器区间是前闭后开的
    for(const auto& e : s)
    {
        cout << e << " ";
    }
    cout << endl;
}
image-20230513124615330

除了set之外,在set头文件中还包含了一个容器:multisetmultiset使用文档

multiset与set极其相似,唯一的不同点就是multiset支持在内部插入重复的数据。

void Test5()
{
    vector<int> v = {1,3,5,2,6,3,5,1};
    set<int> s(v.begin(), v.end());
    multiset<int> ms(v.begin(), v.end());
    cout << "     set:";
    for(const auto& e : s)
    {
        cout << e << " ";
    }
    cout << endl;
    cout << "multiset:";
    for(const auto& e : ms)
    {
        cout << e << " ";
    }
    cout << endl;
}
image-20230513133733679

这里补充一个在set里面没有介绍的接口:count:计算val在multiset中的个数,也可以用来判断set中是否存在该元素。

image-20230513134156562


3. 树形结构的关联式容器——map

map使用文档

image-20230513134546858

  1. map是关联式容器它按照特定的次序(按照key来比较)存储由键值key和值value组合而成的元素
  2. 在map中key是排序和唯一标识的元素,value中存放的是与key相关的内容,key和value的类型不需要相同;在map中存放的类型不是key也不是value,而是一个pair对象,pair对象中的内容是key和value,在实现上是一个typedef,typedef pair<const key,value> value_type.
  3. 在map内部的元素存放位置严格按照key的强弱顺序排列,强弱顺序的比较规则又仿函数Compare控制,默认的仿函数是less
  4. map中通过键值访问单个元素的速度通常比unordered_map容器慢,但map允许根据顺序对元素进行直接迭代(即对map中的元素进行迭代时,可以得到一个有序的序列)
  5. map支持下表访问符[],即通过key访问到value
  6. map的底层是一个平衡搜索树(红黑树)

1. 默认成员函数

image-20230513141202305

image-20230513141248409

image-20230513141316566

默认成员函数和set的用法非常相似,这里就不过多介绍了

2. 迭代器

image-20230513143742429

迭代器的使用方式也是极其相似的,也就不多介绍了,有需要的朋友直接看文档即可

3. 容量与数据访问

函数原型解释
bool empty() const判断map是否为空,如果为空,返回true,否则返回false
size_type size() const返回map中的元素个数,数据类型是size_type(unsigned int)
mapped_type& operator[] (const key_type& k);返回key对应的value,这个value可以被修改

在map中有一个独特的数据访问方式:map中重载了operator[]操作符,可以通过key作为类似下标的方式访问到对应的value

对于这个特性,这里还有一种很新奇的的用法

/统计单词出现次数
vector<string> vs = {"apple", "banana", "apple","orange", "watermelon", "orange", "apple", "watermelon", "watermelon", "apple", "orange", "apple"};
map<string,int> m1;
for(const auto& e : vs)
{
  m1[e]++;
}

🙋当key不在map中时,通过operator获取对应value时会发生什么问题?

image-20230513153214546

通过查看文档可知,如果使用的key不在map里面的时候,会自动调用insert函数插入一个key,然后使用value的默认构造创建一个值,构造一个pair对象,然后插入到map中。与其不同的是,at接口遇到这种情况的时候就会抛出异常。

这个原因也就是上面的新奇用法的原理。

void Test6()
{
    vector<string> vs = {"apple", "banana", "apple","orange", "watermelon", "orange", "apple", "watermelon", "watermelon", "apple", "orange", "apple"};
    map<string,int> m1;//默认构造
    for(const auto& e : vs)//统计次数的方式
    {
        m1[e]++;
    }
    map<string,int> m2(m1.begin(), m1.end());//迭代器区间
    map<string,int> m3(m1);//拷贝构造
    m1 = m3;//赋值重载
    cout << "---------------m1---------------" << endl;
    for(const auto& e : m1)
    {
        cout << e.first << ":" << e.second << endl;
    }
    cout << "---------------m2---------------" << endl;
    for(const auto& e : m2)
    {
        cout << e.first << ":" << e.second << endl;
    }
    cout << "---------------m3---------------" << endl;
    for(const auto& e : m3)
    {
        cout << e.first << ":" << e.second << endl;
    }
}
image-20230513154028887

4.数据修改

image-20230513153537623

数据的修改部分,只看接口的话,发现和set基本一致,用法之类的也都是一致的,没有什么特殊情况,所以也就不多说了。直接上示例:

void Test7()
{
    map<string, string> dict;
    dict.insert(make_pair("left", "左边"));
    dict.insert(make_pair("right", "右边"));
    dict.insert(make_pair("string", "字符串"));
    dict.insert(make_pair("sort", "排序"));
    for(const auto& e : dict)
    {
        cout << e.first << ":" << e.second << endl;
    }
    cout << "------after erase------" << endl;
    dict.erase("insert");
    for(const auto& e : dict)
    {
        cout << e.first << ":" << e.second << endl;
    }
    cout << "------after swap------" << endl;
    map<string, string> dict_swap;
    dict_swap.insert(make_pair("swap", "交换"));
    dict.swap(dict_swap);
    cout << "dict:" << endl;
    for(const auto& e : dict)
    {
        cout << e.first << ":" << e.second << endl;
    }
    cout << "dict_swap:" << endl;
    for(const auto& e : dict_swap)
    {
        cout << e.first << ":" << e.second << endl;
    }
    cout << "------after clear------" << endl;
    dict.clear();
    for(const auto& e : dict)
    {
        cout << e.first << ":" << e.second << endl;
    }
}
image-20230513155359566

5. 其他操作接口

image-20230513155509165

这里的操作接口与set的接口完全一样,就不过多赘述了。

<map>头文件中还包含了multimap这个容器,同样的multimap和map的用法完全基本相同,唯一的区别就是multimap允许重复的key值存在。和set与miltiset的关系一样,有需要的可以去对照上文的set和multiset。


本节完。。。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/521562.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

vue-5:router

router路由配置&#xff0c;使用 在vue-cli构建的vue单页面应用中&#xff0c;需要借助vue-router库实现路由功能 配置路由 (构建项目时要下载) router文件夹下创建&#xff1a;index.js&#xff0c;routerConfig.js配置路由 路由懒加载&#xff1a; 按需加载&#xff1a;…

轻松掌握线程基础知识和四种创建方式及区别

1、线程与进程 2、并行与并发 3、创建线程 1、方式一&#xff1a;继承Thread类 2、方式二&#xff1a;实现Runnable接口 3、方式三&#xff1a;实现Callable接口 4、方式四&#xff1a;线程池创建线程&#xff08;项目中使用的方式&#xff09; 5、Runnable和Callable区别 6、…

在 Windows 10/11、7/8 上清空后从回收站恢复已删除文件的 6 种方法

Windows&#xff08;包括 Windows 11、10、8、7 和 Vista&#xff09;上的回收站用于回收您打算删除的不需要的文件。如果您删除了一些重要的文件或文件夹并且不小心清空了回收站&#xff0c;您仍然有机会恢复从回收站中删除的文件。这是一个教程&#xff0c;将阐明“如何在清空…

HTML第一天

HTML第一天 我们接下来是进行的网页开发网页的相关概念: 什么是网页?什么是HTML?网页的形成? 什么是网页&#xff1a; 1.网站是指在因特网上根据一定的规则&#xff0c;使用 HTML 等制作的用于展示特定内容相关的网页集合。 2.网页是网站中的一“页”&#xff0c;通常是…

钓鱼圈子钓点钓场鱼漂钓位小程序开发

钓鱼圈子钓点钓场鱼漂钓位小程序开发 功能: 关注好友功能。一键导航至钓鱼点。学习交流。鱼票功能是本系统的—大亮点&#xff0c;此功能可应用于鱼场举办活动比赛以及日常预定位置&#xff0c;在小程序进行预定&#xff0c;线下核销。系统拥有商城功能&#xff0c;可以为运营…

深度学习环境配置系列文章(三):配置VS Code和Jupyter的Python环境

深度学习环境配置系列文章目录 第一章 专业名称和配置方案介绍 第二章 Anaconda配置Python和PyTorch 第三章 配置VS Code和Jupyter的Python环境 第四章 配置Windows11和Linux双系统 第五章 配置Docker深度学习开发环境 第三章文章目录 深度学习环境配置系列文章目录前言一、VS…

C语言中链表经典面试题目——复制带随机指针的链表

&#x1f436;博主主页&#xff1a;ᰔᩚ. 一怀明月ꦿ ❤️‍&#x1f525;专栏系列&#xff1a;线性代数&#xff0c;C初学者入门训练&#xff0c;题解C&#xff0c;C的使用文章&#xff0c;「初学」C&#xff0c;数据结构​​​​​​​ &#x1f525;座右铭&#xff1a;“不…

计算机数据表示和数据转换

1、计算机数据表示和数据转换 送入计算机的数字、字母和符号等信息必须转换成0、1组合的二进制形式形式才能被计算机所接收、存储和运算。能够进行计算的数据并且能得出一个明确的数值叫数值数据&#xff0c;其余信息是非数值数据。 1.1 数值数据的表示 数值数据的计数方式是进…

SpringBoot的自动装配

前言 众所周知&#xff0c;SpringBoot的自动装配是其核心功能之一&#xff0c;SpringBoot提供了许多自动配置类&#xff0c;我们通常会有这样的一个概念&#xff1a;“当应用程序启动时&#xff0c;SpringBoot会扫描路径上的自动配置类进行加载&#xff0c;从而大大简化了项目…

小白量化《穿云箭集群量化》(6)巡航导弹策略

小白量化《穿云箭集群量化》&#xff08;6&#xff09;响尾蛇导弹 响尾蛇导弹是非常著名的武器装备&#xff0c;响尾蛇导弹发射者只需雷达瞄准和发射动作&#xff0c;发射动作完成尽快脱离战场保全自身安全。响尾蛇导弹会自动追踪敌机&#xff0c;直至击毁敌机。 证券交易犹如…

MySQL基础(三十一)数据库其它调优策略

1 数据库调优的措施 1.1 调优的目标 尽可能 节省系统资源 &#xff0c;以便系统可以提供更大负荷的服务。&#xff08;吞吐量更大&#xff09;合理的结构设计和参数调整&#xff0c;以提高用户操作 响应的速度 。&#xff08;响应速度更快&#xff09;减少系统的瓶颈&#xf…

day37_jdbc

今日内容 上课同步视频:CuteN饕餮的个人空间_哔哩哔哩_bilibili 同步笔记沐沐霸的博客_CSDN博客-Java2301 零、 复习昨日 零、 复习昨日 见晨考 一、作业 package com.qf.homework;import com.qf.model.User;import java.sql.Connection; import java.sql.DriverManager; impo…

【笔试强训】(红与黑,五子棋,走迷宫)DFS+BFS算法解析

博主简介&#xff1a;想进大厂的打工人博主主页&#xff1a;xyk:所属专栏: 笔试强训专栏 深度优先遍历&#xff08;Depth First Search, 简称 DFS&#xff09; 与广度优先遍历&#xff08;Breath First Search&#xff09;是图论中两种非常重要的算法。 本文就以习题的方式来给…

STM32F10X--EXTI--外部中断/事件控制器

一、EXTI是什么&#xff1f; EXTI&#xff08;External interrupt/event controller&#xff09;—外部中断/事件控制器&#xff0c;管理了控制器的20 个中断/事 件线。每个中断/事件线都对应有一个边沿检测器&#xff0c;可以实现输入信号的上升沿检测和下降沿的 检测。EXTI 可…

SpringMVC的基础知识

创建SpringMVC项目 SpringMVC项目其实和SpingBoot项目差不多,就多引入了一个SpringWeb项目而已拉 可以看这篇博客,创建的就是一个SpringMVC项目--创建项目の博客 SpringMVC是啥 Spring是啥相信大家都了解 啥是MVC呢?MVC是Model View Controller的缩写 我们分开看这三个词Model…

【框架源码】Spring源码核心注解@Conditional原理及应用

1.什么是Conditional注解 Conditional来源于spring-context包下的一个注解。通过Conditional配置一些条件判断&#xff0c;当所有条件都满足时&#xff0c;被该Conditional注解标注的目标才会被Spring处理。 例如根据当前环境、系统属性、配置文件等条件来决定是否注册某个Bea…

PostgreSQL-如何创建并发索引

索引简介 索引是数据库中一种快速查询数据的方法。索引中记录了表中的一列或多列值与其物理位置之间的对应关系&#xff0c;就好比一本书前面的目录&#xff0c;通过目录中页码就能快速定位到我们需要查询的内容。 建立索引的好处是加快对表中记录的查找或排序&#xff0c;但…

Mysql进阶-索引事务相关

文章目录 数据库存储引擎INNODBMYISAM 索引索引分类索引语法SQL性能分析SQL执行频率慢查询profile详情explain 执行计划**Etrax**(额外信息&#xff09;using index conditionusing where&#xff1b;using indexusing where 索引使用规则最左前缀法则范围查询 索引失效情况1.索…

conda故障处理

【已解决】subprocess-exited-with-error 准备元数据(setup.py)…错误 错误:subprocess-exited-with-error python setup.py egg_info运行失败。 │退出代码:10 <╰>[1行输出] 请指定——curl-dir/path/to/built/libcurl [输出结束] 注意:此错误源自子进程&#xf…

Halcon 形态学(膨胀(Dilation)、腐蚀(Erosion))

文章目录 1 形态学概念2 膨胀(Dilation) 算子介绍3 腐蚀(Erosion)算子介绍4 膨胀腐蚀 示例15 腐蚀膨胀 示例26 示例原图7 补充:结构元素概念1 形态学概念 图像的形态学处理是对图像的局部像素进行处理,用于从图像中提取有意义的局部图像细节。 通过改变局部区域的像素形态…