初识C++ · map和set的使用

目录

前言:

1 set

2 map


前言:

在前面阶段,我们已经学习了stl里面的部分容器,比如vector,list,deque等,这些容器都被称为序列式容器,也就是每个值之间式没有关联的,那么今天介绍的容器,map和set,是关联式容器,即每个值之间是有关联的,关联式容器在数据存储方面和序列式容器没有什么大差别,但是在数据检索上就很有用了,其中map的key - value模型存的键值对在数据检索方面的效率是很高的。

上篇介绍了二叉搜索树,当数据接近有序的时候,二叉搜索树的效率就比较低了,接近logN了,那么map和set就是基于红黑树实现的一种结构,它在普通二叉树的基础上加成了平衡。

话不多说,开始介绍。


1 set

set的底层模型是key模型,即每个节点只有一个类型的值,我们先看定义:

set的模板参数有3个,第一个是key_type,也就是每个值的类型,compare是仿函数,这个点在优先级队列有提及,第三个参数是空间配置器,基本stl的容器都是人手一个。

左边注释的那些,都是被typedof的,文档解释如下:

现在我们进入到set的成员函数部分。

有关构造 析构 赋值

第一个构造函数,是默认构造函数,不需要传参,我们可以理解为,空构造,第二个构造函数,是迭代器区间构造,我们可以使用其他容器的迭代器来进行构造,第三个构造就是拷贝构造了:

int main()
{vector<int> v1({ 1,2,3,4 });set<int> s1;set<int> s2(v1.begin(), v1.end());return 0;
}

析构没有什么要注意的,set支持直接赋值的,所以可以:

int main()
{vector<int> v1({ 1,2,3,4 });set<int> s1;set<int> s2(v1.begin(), v1.end());s1 = s2;return 0;
}

有关迭代器部分

如文档解释的iterator一样,set支持的是双向迭代器,所以有begin,就有rbegin,cbegin也是不可少的,crbegin也有,使用方面上和vector那些没有什么差别:

int main()
{vector<int> v1({ 1,2,3,4 });set<int> s1;set<int> s2(v1.begin(), v1.end());s1 = s2;set<int>::iterator it1 = s2.begin();while (it1 != s2.end()){cout << *it1 << " " << endl;it1++;}cout << endl;return 0;
}

但是这是set,set是key模型,也就意味着我们想要修改里面的值的时候,就会报错:

有关capacity部分

无非就是判断是否为空,大小多少,最大的空间开辟都到多少,使用和序列式容器一样的,就不多介绍了。

有关Modifiers部分

这里面的emplace和insert的作用差不多的,留到C++ 11里面介绍,因为里面涉及到了右值引用。

与序列式容器不同的是,这没有头插尾插,只有一个insert,删除也是同理,因为结构的特性,insert erase使用如下:

int main()
{set<int> s1;s1.insert(1);s1.insert(2);s1.insert(3);s1.insert(3);s1.insert(4);s1.erase(1);s1.erase(2);s1.erase(3);s1.erase(18);return 0;
}

有人发现不对了吧,插入有问题吗?没有问题,但是有个问题需要注意,请问插入了两个3,里面有几个3呢?在二叉搜索树的模拟实现中,就提及到了,不允许数据的冗余性,但是呢,这是也是不会报错的,同理,在模拟实现删除的时候,删除失败就会返回false,这里也是同理,所以都不会报错:

int main()
{set<int> s1;s1.insert(1);s1.insert(2);s1.insert(3);s1.insert(3);s1.insert(4);set<int>::iterator it1 = s1.begin();while (it1 != s1.end()){cout << *it1 << " " << endl;it1++;}cout << endl;s1.erase(1);s1.erase(2);s1.erase(3);s1.erase(18);set<int>::iterator it2 = s1.begin();while (it2 != s1.end()){cout << *it2 << " " << endl;it2++;}cout << endl;return 0;
}

最后都是可以正常打印:

有关operations部分

 这里介绍find count lower_bound upper_bound:

find的返回值是iterator,也就是返回的那个值的迭代器,我们就可以用于删除,或者是遍历,删除也可以用迭代器来删除,是erase的第二个重载:

int main()
{set<int> s1{ 1,2,3,4,5,6,7,8 };set<int>::iterator it1 = s1.find(3);set<int>::iterator it2 = s1.find(8);s1.erase(it2);while (it1 != s1.end()){cout << *it1 << endl;it1++;}return 0;
}

count的使用也就是计数了,但是因为不允许数据的冗余,每个值顶多只有一个,所以count的值不是1就是0,那也是有作用的:

int main()
{set<int> s1{ 1,2,3,4,5,6,7,8 };if (s1.count(1) == 1){cout << "s1里面有1这个元素" << endl;}return 0;
}

这就是count的使用。

对于lower_bound upper_bound 的使用,它们是经常在一起使用的,它们形成的是一个左闭右开的区间,和迭代器的使用保持一致,左闭右开:


int main()
{std::set<int> myset;std::set<int>::iterator itlow, itup;for (int i = 1; i < 10; i++)myset.insert(i * 10); itlow = myset.lower_bound(30);itup = myset.upper_bound(60);myset.erase(itlow, itup);std::cout << "myset contains:";for (std::set<int>::iterator it = myset.begin(); it != myset.end(); ++it)std::cout << ' ' << *it;return 0;
}

所以最终打印就是把30 - 60的部分删除了,但是这里返回的迭代器一定不是60,是70。

对于set来说,数据冗余是不允许的,但是有特定的容器允许:

即multiset,multi是多样的意思,这个容器除了允许数据冗余之外,没有其他更改。


2 map

map是key - value模型:

可以看到模板参数有4个,其中有key T 仿函数和空间配置器。

那么有了set的铺垫,这里我们就选几个函数来介绍:

在map里面,我们着重需要注意的是insert和[]重载,先来看insert:

insert的第一个重载就是涉及到了pair<iterator,bool>,那么什么是pair呢?

pair有两个参数,而在key - value模型中,我们实现的时候是使用定义两个变量的方法,实际操作的时候是使用的pair参数,我们将key - value存放pair里面,简称为键值对。

在stl里面关于pair的定义如下:

template <class T1, class T2>
struct pair
{
typedef T1 first_type;
typedef T2 second_type;
T1 first;
T2 second;
pair(): first(T1()), second(T2())
{}
pair(const T1& a, const T2& b): first(a), second(b)
{}
};

有一个first 一个second ,我们存放map的时候,first就是key,second就是value。

那么插入可以:

int main()
{map<int,int> m1;pair<int, int> kv1(1,2);m1.insert(kv1);return 0;
}

也可以:

	m1.insert(pair<int,int>(1, 4));

但是呢,创建一个有名对象和一个匿名对象都有人觉得麻烦了,于是有这么一个方法:

make_pair方法,返回值是一个pair类型,我们可以直接使用该函数来插入:

m1.insert(make_pair(2, 4));

 但是还是麻烦了,对于多参数类型的构造函数来讲,我们可以直接隐式类型转换:

m1.insert({ 2, 4 });

 这里可不是initializer_list的使用,因为pair的参数有两个,构造需要两个参数,对于多参数的构造函数来讲直接隐式构造就可以了,这里可不是vector的构造。

但是呢,initializer_list可以用到map的构造里面去,这种构造是在C++11里面才引入的:

map<string, string> m2{ {"left","左边"}, {"right","右边"}, {"Hello","你好"}, {"main","主要的"}};

这种构造也是被允许的,和vector的构造是一样的。

对于operator[]:

这个可是个大头,在vector里面,[]是下标随机访问的函数,在这里,只能说有一点点像,具体我们先看最下面的那段代码,函数等效于:

(*((this->insert(make_pair(k,mapped_type()))).first)).second

我们从里面看,make_pair是创建一个键值对,里面的参数是k,mapped_type,其实第二个参数不给的话就是空构造,但是K是有的。简单翻译就是插入first,返回second。

同时我们根据文档可以看到,返回值是mapp_type的引用,也就是键值对的second的引用,此时,我们再看看insert的文档:

在insert的返回值,我们可以看到这段话,简单翻译过来就是,键值对的第二个参数是bool类型的,插入成功,bool值就会变成true类型,如果插入失败,即结构里面已经有了该key,bool类型就会变成false类型,但是无论如何,键值对的第一个参数,迭代器类型都会指向所在key的迭代器或者是新插入的key的所在的迭代器类型,那么简单模拟实现一下就是:

V& operator[](const T& key)
{pair<iterator, bool> ret = insert(make_pair(key, V()));iterator it = ret.first;return ret->second;
}

返回的值是第二个参数的引用,也就是说间接可以修改第二个参数了,这点还是很不错的,但是如果我们不使用返回值,可以相当于插入使用,所以[]的使用可以:

int main()
{map<string, string> m2{ {"left","左边"}, {"right","右边"}, {"Hello","你好"}, {"main","主要的"}};m2["world"];return 0;
}

这是一种插入,但是基本上不这么用。

int main()
{map<string, string> m2{ {"left","左边"}, {"right","右边"}, {"Hello","你好"}, {"main","主要的"}};m2["Hello"] = "哈喽";return 0;
}

这是一种修改。

int main()
{map<string, string> m2{ {"left","左边"}, {"right","右边"}, {"Hello","你好"}, {"main","主要的"}};m2["Hello"] = "哈喽";cout << m2["Hello"] << endl;return 0;
}

这是一种查找。

总结来说可以实现的操作可以有查找,修改,插入 + 修改,毕竟如果key有的话也不会插入,就相当于修改了。

[]的使用是很厉害的,可能有人会觉得和vector的使用有点像,但差了很多,自行体会哈哈哈。

当然,这里也有multimap,和set那边是一样的,下来可以自己试试。

总结:

set + map的使用可以当去重,因为插入多个数据的时候,不会插入多个数据,也可以用来排序,也可以用来求差集,交集,这点都是因为set 和 map没有数据的冗余。


感谢阅读!

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

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

相关文章

IGV.js | 载入自己下载的gtf文件

1.安装 htslib-1.20 https://www.htslib.org/doc/tabix.html J3$ cd ~/Downloads/ $ wget https://github.com/samtools/htslib/releases/download/1.20/htslib-1.20.tar.bz2 $ tar jxvf htslib-1.20.tar.bz2编译安装&#xff1a; $ cd htslib-1.20/ $ ./configure --prefix/…

vue的三大核心知识点

响应式&#xff1a; 监听data属性getter setter(包括数组)模板编译&#xff1a; 模板到render函数再到vnodevdom&#xff1a; patch(elem, vnode)和patch(vnode, newVnode) vue组件初次渲染过程 解析模板为render函数&#xff08;或在开发环境已完成&#xff0c;vue-loader&a…

WIX Toolset 3.11 对本地化的支持方案

1.准备主题文件和本地化文件 WIX Toolset种主题文件为xml文件&#xff0c;负责配置控件的布局&#xff0c; 本地化文件为wxl文件&#xff0c;负责配置待加载的字符串&#xff0c;主题文件根据ID加载需要显示的文字内容。考虑到英文和中文字符长度大小不一&#xff0c;所以这里…

渗透测试——prime1靶场实战演练{常用工具}端口转发

文章目录 概要信息搜集 概要 靶机地址&#xff1a;https://www.vulnhub.com/entry/prime-1,358 信息搜集 nmap 扫网段存活ip及端口 找到除了网关外的ip&#xff0c;开放了80端口&#xff0c;登上去看看 是一个网站&#xff0c;直接上科技扫一扫目录 python dirsearch.py -u …

尝试带你理解 - 进程地址空间,写时拷贝

序言 在上一篇文章 进程概念以及进程状态&#xff0c;我们提到了 fork 函数&#xff0c;该函数可以帮我们创建一个子进程。在使用 fork 函数时&#xff0c;我们会发现一些奇怪的现象&#xff0c;举个栗子&#xff1a; 1 #include <stdio.h>2 #include <unistd.h>3 …

跟《经济学人》学英文:2024年07月20日这期 The Russell 2000 puts in a historic performance

Why investors have fallen in love with small American firms The Russell 2000 puts in a historic performance 罗素2000指数&#xff1a; 罗素2000指数&#xff08;英语&#xff1a;Russell 2000 Index&#xff09;为罗素3000指数中收录市值最小的2000家&#xff08;排序…

学习笔记 韩顺平 零基础30天学会Java(2024.7.25)

P425 枚举类引出 举了一个例子&#xff0c;季节类创建对象&#xff0c;但是根据Java的规则&#xff0c;可以设置春夏秋冬以外的对象&#xff0c;而且可以修改&#xff0c;这样就会不符合实际&#xff0c;因此引出枚举 P426 自定义枚举类 1.构造器私有化&#xff0c;使外面没有办…

深入解析 GPT-4o mini:强大功能与创新应用

&#x1f4e2;博客主页&#xff1a;https://blog.csdn.net/2301_779549673 &#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01; &#x1f4e2;本文由 JohnKi 原创&#xff0c;首发于 CSDN&#x1f649; &#x1f4e2;未来很长&#…

C++ 列式内存布局数据存储格式 Arrow

Apache Arrow 优点 : 高性能数据处理&#xff1a; Arrow 使用列式内存布局&#xff0c;这特别适合于数据分析和查询操作&#xff0c;因为它允许对数据进行高效批量处理&#xff0c;减少CPU缓存未命中&#xff0c;从而提升处理速度。 零拷贝数据共享&#xff1a; Arrow …

【YashanDB知识库】yasdb jdbc驱动集成druid连接池,业务(java)日志中有token IDENTIFIER start异常

问题现象 客户的java日志中有如下异常信息&#xff1a; 问题的风险及影响 对正常的业务流程无影响&#xff0c;但是影响druid的merge sql功能&#xff08;此功能会将sql语句中的字面量替换为绑定变量&#xff0c;然后将替换以后的sql视为同一个&#xff0c;然后用做执行性能统…

Vue3扁平化Tree组件的前端分页实现

大家好&#xff0c;我是小卷。得益于JuanTree的扁平化设计&#xff0c;在数据量很大的情况下除了懒加载&#xff0c;使用前端分页也是一种解决渲染性能问题的可选方案。 用法 要实现的文档&#xff1a; 分页效果&#xff1a; 实现 新增属性&#xff1a; 组件setup方法中新增…

程序员加班现象:成因、影响与应对策略

&#x1f34e;个人博客&#xff1a;个人主页 &#x1f3c6;个人专栏&#xff1a;日常聊聊 ⛳️ 功不唐捐&#xff0c;玉汝于成 目录 前言 正文 加班的成因 加班的影响 应对策略 结语 我的其他博客 前言 在现代科技行业中&#xff0c;加班现象已成为一个普遍存在的问题…

配置sublime的中的C++编译器(.sublime-build),实现C++20

GCC 4.8: 支持 C11 (部分) GCC 4.9: 支持 C11 和 C14 (部分) GCC 5: 完全支持 C14 GCC 6: 支持 C14 和 C17 (部分) GCC 7: 支持 C17 (大部分) GCC 8: 完全支持 C17&#xff0c;部分支持 C20 GCC 9: 支持更多的 C20 特性 GCC 10: 支持大部分 C20 特性 GCC 11: 更全面地支持 C20 …

ES中的数据类型学习之ARRAY

Arrays | Elasticsearch Guide [7.17] | Elastic 中文翻译 &#xff1a;Array Elasticsearch 5.4 中文文档 看云 Arrays In Elasticsearch, there is no dedicated array data type. Any field can contain zero or more values by default, however, all values in the a…

SpringBoot 自动配置原理

一、Condition Condition 是在 Spring 4.0 增加的条件判断功能&#xff0c;通过这个可以功能可以实现选择性的创建 Bean 操 作。 思考&#xff1a; SpringBoot 是如何知道要创建哪个 Bean 的&#xff1f;比如 SpringBoot 是如何知道要创建 RedisTemplate 的&#xff1f; …

mysql的B+树索引结构介绍

一、B树 特性&#xff1a; 所有的叶子结点中包含了全部关键字的信息&#xff0c;非叶子节点只存储键值信息&#xff0c;及指向含有这些关键字记录的指针&#xff0c;且叶子结点本身依关键字的大小自小而大的顺序链接&#xff0c;所有的非终端结点可以看成是索引部分&#xff0…

MySQL数据库基本用法

了解数据库基本概念 什么是数据库&#xff1f; • 长期存放在计算机内&#xff0c;有组织、可共享的大量数据的集合&#xff0c;是一个数据“仓库” MySQL数据库的特点 • 开源免费&#xff0c;小巧但功能齐全 • 可在Windows和Linux系统上运行 • 操作方便&#xff0c;…

昇思25天学习打卡营第22天|munger85

LSTMCRF序列标注 我们希望得到这个模型来对词进行标注&#xff0c;B是开始&#xff0c;I是实体词的非开始&#xff0c;O是非实体词。 我们首先需要lstm对序列里token的记忆&#xff0c;和计算每个token发到crf的分数&#xff0c;发完了再退出来&#xff0c;最后形成1模型。那么…

免费可视化工具大显身手:公司财务报表一键生成

面对海量的财务数据&#xff0c;如何快速、准确地提炼出有价值的信息&#xff0c;并以直观易懂的方式呈现给管理层及利益相关者&#xff0c;成为了每一家企业面临的重大挑战。 传统财务报表编制过程繁琐&#xff0c;不仅耗时耗力&#xff0c;还容易出错。而一些可视化工具&…

Java学习笔记(四)控制流程语句、循环、跳转控制语句

Hi i,m JinXiang ⭐ 前言 ⭐ 本篇文章主要介绍Java控制流程语句、循环、跳转控制语句使用以及部分理论知识 &#x1f349;欢迎点赞 &#x1f44d; 收藏 ⭐留言评论 &#x1f4dd;私信必回哟&#x1f601; &#x1f349;博主收将持续更新学习记录获&#xff0c;友友们有任何问题…