初识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,一经查实,立即删除!

相关文章

【笔记本触摸屏】超级好用技巧

选中文字&#xff1a;点一下要复制的文字开头&#xff0c;按住shift键不放&#xff0c;然后点一下你想要的文字结尾滚动&#xff1a;双指向 水平 或者 垂直 方向滑动放大或者缩小: 将两个手指放在触摸板上&#xff0c;让后收缩后者拉伸显示更多命令&#xff08;类似于右键单击&…

四十九、 通过境内数据交易所进行跨境数据贸易应考虑哪些跨境数据合规问题?

根据中国信通院数据显示&#xff0c;2023 年我国数字经济规模可达 56.1 万亿元, 数字经济占 GDP 比重接近于第二产业&#xff0c;占国民经济的比重&#xff0c;达到 40%以上。伴随数字技术兴起以及各项数字经济相关的政策和法律的落地&#xff0c;以跨境数据流动为底层支撑的跨…

智能音箱和普通音箱有什么区别

智能音箱和普通音箱在多个方面存在显著的区别&#xff0c;主要包括设计目的、功能特点、连接方式、音质表现以及交互方式等。 一、设计目的和功能特点 智能音箱&#xff1a;设计目的不仅仅是为了播放音乐&#xff0c;更重要的是集成了语音识别和语音交互功能&#xff0c;成为…

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/…

ts -> class -> abstract

在TypeScript中&#xff0c;你可以直接使用abstract关键字来定义抽象类和抽象方法。抽象类不能被实例化&#xff0c;而抽象方法必须在派生类中被实现。以下是一个具体的例子&#xff1a; abstract class Animal {name: string;constructor(name: string) {this.name name;}//…

C#中的Action

C#中的Action是一种委托类型&#xff0c;‌用于引用不返回值的方法。‌Action可以接受0到16个参数&#xff0c;‌并且不返回任何值。‌它是一种通用的委托类型&#xff0c;‌非常方便用于处理不同参数和不同函数签名的情况。‌Action的用法包括声明Action委托类型、‌创建Actio…

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;排序…

Linux 常用命令详解:从基础操作到进阶应用

Linux 常用命令详解&#xff1a;从基础操作到进阶应用 简介 Linux 是一个强大且灵活的操作系统&#xff0c;它在服务器、开发环境和个人计算机中得到了广泛的应用。Linux 的命令行界面提供了丰富的工具和命令&#xff0c;可以帮助用户高效地管理系统、处理文件、监控性能和进…

WebKit的暗黑魅力:全面拥抱Dark Mode

WebKit的暗黑魅力&#xff1a;全面拥抱Dark Mode 在当今数字时代&#xff0c;用户越来越注重个性化体验和视觉舒适度。暗黑模式&#xff08;Dark Mode&#xff09;作为一种新兴的界面风格&#xff0c;以其减轻视觉疲劳和节省电量的特点&#xff0c;迅速受到用户的青睐。WebKit…

学习笔记 韩顺平 零基础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;未来很长&#…

【Vue实战教程】之Vue项目中的异步请求

Vue的异步请求 1 axios的安装与使用 Axios是一个基于promise的HTTP库&#xff0c;主要用来向服务端发起请求&#xff0c;可以在请求中做更多可控的操作&#xff0c;例如拦截请求等。 Axios可以使用在浏览器和node.js中&#xff0c;Vue、React等前端框架的广泛普及&#xff0c…

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;加班现象已成为一个普遍存在的问题…