【C++】map、set,multiset和multimap的使用及底层原理【完整版】

目录

一、map和set的使用

1、序列式容器和关联式容器

2、set的使用讲解

 3、map的使用讲解

二、multiset和multimap 

1、multiset和multimap的使用

2、OJ题:前k个高频单词


一、map和set的使用

1、序列式容器和关联式容器

序列式容器:vector/list/string/deque

序列式容器才支持push等操作,关联式容器不支持

关联式容器:map/set/unordered_map/unordered_set

set和map底层实现平衡搜索二叉树


2、set的使用讲解

  • set就是搜索树中的key模型
  • set的特性:①、会对插入的数据自动排序 ②、set是不允许修改值的 ③、set中不允许出现重复的数值,即使存在,也只会留一个
  • set的遍历:①、迭代器遍历 ②、范围for遍历(因为支持迭代器遍历就一定支持范围for)
  • set的拷贝构造
  • set的插入只有insert,其没有push、pop等,因为它是关联式容器
  • set的find,find找到了会返回被查找元素的迭代器,没找到返回end(),故应检查找没找到
  • 那set的find和库里面提供的find有什么区别呢?
  • 都可实现查找,区别在于效率
  • set是搜索二叉树的:时间复杂度:O(logN),而算法中的是O(N)
  • 算法中的find是个模板,其实现是为了所有容器可以通用它,故set尽量用自己的find 
  • set的删除
  • ①、erase(待删除位置的迭代器)  ②、erase(待删除数据) ③、erase(s.begin(), s.end())【即迭代器头和尾,其效果等价于clear   】

因为setkey模型,是看在不在,如果把中国所有人的信息存入到set中,最多搜索次数才31次,因为搜索二叉树的效率:O(logN)2^31就=20多亿了,这个效率是非常好的

代码如下:

void test_set()
{set<int> s;s.insert(3);s.insert(1);s.insert(4);s.insert(3);s.insert(7);//set : 排序+去重set<int>::iterator it = s.begin();while (it != s.end()){cout << *it << " ";++it;}cout << endl;//支持迭代器,就支持范围forfor (auto e : s){cout << e << " ";}cout << endl;set<int> copy(s);//set的深拷贝for (auto& e : copy){cout << e << " ";}cout << endl;//auto pos = s.find(3);//可用auto推导类型//set<int>::iterator pos = s.find(3);//find查找返回迭代器 find找到了会返回元素的迭代器,没找到返回end()//if (pos != s.end())//{//找到了才能删除//	s.erase(pos);//erase会删除迭代器位置的数据//}//若erase直接给值,若值不存在,也不会报错,但迭代器必须存在那个位置set<int>::iterator pos = find(s.begin(), s.end(), 3);//使用算法中的findif (pos != s.end()){s.erase(pos);}for (auto& e : s){cout << e << " ";}cout << endl;
}

运行结果: 

  


 3、map的使用讲解

  • map就是搜索树中的key/value模型
  • map的遍历①、迭代器遍历 ②、范围for遍历
  • map类型pairpair存的一个是key的,一个是value的类型
  • map的构造函数:①、pair构造函数 ②、make_pair函数模板构造一个pair对象
void test_map1()
{map<int, int> m;//m.insert(1, 1);//编译不通过m.insert(pair<int, int>(1, 1));//pair构造函数,构造一个匿名对象m.insert(pair<int, int>(3, 3));m.insert(pair<int, int>(2, 2));m.insert(make_pair(4, 4));	   //函数模板构造一个pair对象map<int, int>::iterator it = m.begin();while (it != m.end()){	//*it等价于pair,而要访问它的成员cout << it->first << ":" << it->second << " " << endl;//也可以用(*it).first    (*it).second//operator* 返回值是节点中值的引用//operator->返回值是节点中值的指针,即pair<k,v>指针//本质上为了可读性,这里省略了一个->++it; }cout << endl;for (auto& e : m){//first就是key值,即pair中的第一个值,second就是value值,即pair中的第二个值cout << e.first << ":" << e.second << endl;}}

 


  • map构造函数两种方法区别


void test_map2()
{//一般写项目不会把std库中的全引进来,而是如下代码,make_pair明显更加简洁std::map<std::string, std::string> dict;dict.insert(pair<std::string, std::string>("metric", "米制的"));dict.insert(make_pair("potent", "强大的"));dict.insert(make_pair("deplete", "大量减少"));std::map<std::string, std::string>::iterator it = dict.begin();while (it != dict.end()){cout << it->first << ":" << it->second << endl;++it;}cout << endl;
}

可见使用make_pair会使代码更简洁


以下是map的应用统计水果出现的次数【本质是key/value模型的应用

法一:利用map的find(key值查找,不是value值)

void test_map3()
{//用STL中的map怎么统计水果出现的次数呢?string strs[] = { "西瓜","樱桃","苹果","西瓜","西瓜","西瓜","西瓜","苹果" };map<string, int> countMap;for (auto & str : strs){map<string, int>::iterator ret = countMap.find(str);if (ret != countMap.end()){ret->second++;//相当于value++}else{//第一次出现,直接插入value为1countMap.insert(make_pair(str, 1));}}for (auto& e : countMap){cout << e.first << ":" << e.second << endl;}
}


法二、map的operator[ ]求解

我们之前学的容器只有string,vector和deque才有operator[ ],而这里map的operator[ ]还有所不同

下面是operator[ ]底层

可见给operator[ ]一个key值,它返回对应的value值的引用

那就可以把求水果出现的次数代码用operator[ ]实现进一步优化

void test_map3()
{//用STL中的map怎么统计水果出现的次数呢?string strs[] = { "西瓜","樱桃","苹果","西瓜","西瓜","西瓜","西瓜","苹果" };map<string, int> countMap;for (auto& str : strs){//法二、operator[]实现countMap[str]++;//给key值:字符串,返回对应value的引用:次数}for (auto& e : countMap){cout << e.first << ":" << e.second << endl;}
}

法三、map的insert求解

operator[ ]的底层是调用insert实现的,故想了解operator[ ]要先了解insert

insert的其中一个版本是

pair<iterator, bool> insert (const value_type& val);

返回值的意思:

单元素版本:(1)返回pair,其成员pair::first设置为一个迭代器,该迭代器指向新插入的元素或映射中具有等效键的元素。如果插入了新元素,则pair::第二个元素设为true,如果已经存在等效键,则设为false。

理解:

insert对于插入不存在的数据充当插入作用pairfirst指向新插入元素,second设为true,但若插入一个已经存在的数据,insert充当查找作用pairfirst指向之前存在的那个元素,second设为false

利用insert这个版本的特点,我们可以把水果出现的次数再写一个insert的版本

void test_map3()
{//用STL中的map怎么统计水果出现的次数呢?string strs[] = { "西瓜","樱桃","苹果","西瓜","西瓜","西瓜","西瓜","苹果" };map<string, int> countMap;for (auto & str : strs){//法三、insert实现pair<map<string, int>::iterator, bool> ret = countMap.insert(make_pair(str, 1));//也可写为auto ret = countMap.insert(make_pair(str, 1));//如果插入成功,那就说明之前在map中没出现过,value为1即可if (ret.second == false){//插入失败,说明之前存在这个数据,迭代器指向之前出现的那个元素ret.first->second++;//用迭代器访问到这个元素的value值}}for (auto& e : countMap){cout << e.first << ":" << e.second << endl;}
}

那insert是如何实现map的operator[]的?

  • 如果水果不在map中,则[ ]会insert插入pair<str, int()> 等价于 pair<str, 0>,那么返回映射对象(次数)的引用就进行了++1
  • 如果水果在map中,则operator[ ]返回水果对应的映射对象(次数)的引用,对它++

下面讲解下map的operator[ ]多种功能

void test_map3()
{//用STL中的map怎么统计水果出现的次数呢?string strs[] = { "西瓜","樱桃","苹果","西瓜","西瓜","西瓜","西瓜","苹果" };map<string, int> countMap;for (auto & str : strs){//法二、operator[]实现countMap[str]++;//给key值:字符串,返回对应value的引用:次数}countMap["香蕉"];       //插入,因为第一次出现countMap["香蕉"] = 1;   //修改,因为operator[]返回value的引用,故可修改cout << countMap["香蕉"] << endl;//查找,因为香蕉已经存在了countMap["哈密瓜"] = 5; //插入+修改,哈密瓜第一次出现,并对他的value进行了修改map<string, string> dict;dict.insert(make_pair("sort", "排序"));dict["string"];//key为string,value是string类型的构造函数【因为其是缺省值】,即空串  //插入(一般不会这样用)dict["string"] = "字符串";//返回value的引用,可以对其进行修改,能修改是因为返回value的引用 //修改,不算插入因为已存在dict["left"] = "左边";//插入+修改,因为"左边"第一次出现,故插入,插入后又对其value进行了修改for (auto& e : countMap){cout << e.first << ":" << e.second << endl;}
}

注:传参只能传key,不能只传value不传key,因为底层是搜索树,搜索树要用key去比较大小,key只要进去了就不能修改了

一般使用operator[]

  • 插入+修改
  • 修改

一般不会用它去查找,因为如果key不在会插入数据

总结:


二、multiset和multimap 

1、multiset和multimap的使用

 multiset和multimap除了在set和map的基础上支持数据重复出现外,根本没什么区别

void test_multi()
{//与set的区别是允许键值key冗余(重复)multiset<int> ms;ms.insert(3);ms.insert(2);ms.insert(3);ms.insert(1);ms.insert(4);ms.insert(5);for (auto e : ms){cout << e << " ";}cout << endl;auto pos = ms.find(3);cout << *pos << endl;++pos;cout << *pos << endl;++pos;//multi_map和map的区别和set与multi_set的区别一样//额外区别是muti_map没有operator[],因为当有多个相同的可以时,不知道返回哪个key对应的valuemultimap<string, int> mm;mm.insert(make_pair("苹果", 1));mm.insert(make_pair("苹果", 1));mm.insert(make_pair("苹果", 3));mm.insert(make_pair("西瓜", 2));mm.insert(make_pair("西瓜", 1));}

 


2、OJ题:前k个高频单词

思路:

①、先创建个map对象,利用operator[ ]对其中的字符串排序(会按ASCII码排序),那么key值应该是string,因为map是按照key值从低到高排序的 

②、因为出现频率高的在前,且还有重复数据的出现,故使用multimap和仿函数

countMap中的数据插入到multimap中,multimapkey值是int类型的,那相当于multimap按出现频率排序,那出现频率高的就会在前,而出现频率相同的,之前operator[ ]已排好序了,按字典顺序排的,小的ASCII码在前

③、因为返回vector<string>,故只把multimap中的string存入到结果中即可,访问他的string即迭代器位置->second

class Solution {
public:vector<string> topKFrequent(vector<string>& words, int k) {map<string, int> countMap;//统计每个字符串出现了多少次for (auto& e : words){countMap[e]++;//map会自动对key值排序,即对string排序,并修改对应的value值}//但我们现在需对value值排序,即对int排序,因为要找出现频率高的//法一、将pair<string, int>键值对放到vector中,用sort排序,还要写一个//按int比较的仿函数,因为sort是快排实现的,不稳定,排完了,还需对次数相同的按字母排,要存入vector是因为//sort只供支持随机访问的容器使用,如vector、deque//法二、用multimap按次数排序,利用仿函数控制从大到小排multimap<int, string,greater<int>> sortMap;//multimap可以保证数据的重复出现for (auto& kv : countMap){sortMap.insert(make_pair(kv.second, kv.first));//排完序后插入到multimap,其会按int从大到小排//排完后//出现次数高的在前面,而出现次数相同的,之前已用operator[]按string排序了}vector<string> v;auto it = sortMap.begin();while (it != sortMap.end()){if (k == 0)break;v.push_back(it->second);//插入字符串++it;--k;//插入完一个就--}return v;}
};

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

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

相关文章

基于微信小程的流浪动物领养小程序设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言系统主要功能&#xff1a;具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计…

CSS 选择器Day01

CSS 定义&#xff1a;层叠样式表(Cascading Style Sheets&#xff0c;缩写为 CSS)&#xff0c;是一种用于定义网页或文档的外观和样式的标记语言。 CSS是一种 样式表 语言&#xff0c;用来描述 HTML 文档的呈现 (美化内容)。它用于控制文本的字体、颜色、间距、布局、背景等各…

如何使用Docker安装最新版本的Redis并设置远程访问(含免费可视化工具)

文章目录 安装Docker安装Redisredis.conf文件远程访问Redis免费可视化工具相关链接Docker是一种开源的应用容器引擎,使用Docker可以让我们快速部署应用环境,本文介绍如何使用Docker安装最新版本的Redis。 安装Docker 首先需要安装Docker,具体的安装方法可以参考Docker官方文…

C++标准模板库STL——list的使用及其模拟实现

1.list的介绍 list的文档介绍 1. list是可以在常数范围内在任意位置进行插入和删除的序列式容器&#xff0c;并且该容器可以前后双向迭代。 2. list的底层是双向链表结构&#xff0c;双向链表中每个元素存储在互不相关的独立节点中&#xff0c;在节点中通过指针指向 其前一个…

Error: node: unknown or unsupported macOS version: :dunno 错误解决

一、原因 今天安装 brew install node报错了&#xff0c;错误信息如下&#xff1a; 二、解决方案 1&#xff09;查找homebrew-cask安装位置 echo $(brew --repo homebrew/homebrew-cask) // 输出 /opt/homebrew/Library/Taps/homebrew/homebrew-cask2&#xff09;使用 gi…

Win11下无法打开丛林之狐,提示未检测到DirectX 8.1

新装的win11系统&#xff0c;打开丛林之狐提示未检测到DirectX 8.1. 运行dxdiag检查DirectX版本&#xff1a; DX版本已经是12了&#xff1a; 最终参考了这篇文章解决了&#xff1a; 罪恶都市出现XX-directx version 8.1处理方法 - 知乎 控制面板 > 程序 > 启用或关闭Wi…

AI-FGNet降噪算法

上一篇文章介绍AI-CGNet降噪算法和AI-GruNet降噪算法&#xff0c;本篇文章介绍一个新的轻量级降噪做法AI-FGNet。 一、模型结构 AI-FGNet网络相比AI-GruNet&#xff0c;额外添加一层全连接实现特征的维度变换&#xff0c;作为频谱压缩、控制计算量的一种手段。此外&#xff0c…

ICCV 2023|Occ2Net,一种基于3D 占据估计的有效且稳健的带有遮挡区域的图像匹配方法...

本文为大家介绍一篇入选ICCV 2023的论文&#xff0c;《Occ2Net: Robust Image Matching Based on 3D Occupancy Estimation for Occluded Regions》&#xff0c; 一种基于3D 占据估计的有效且稳健的带有遮挡区域的图像匹配方法。 论文链接&#xff1a;https://arxiv.org/abs/23…

2023年十大开源项目:革新技术创新

来源整理 : 小托 | 开源社翻译组PM 翻译 : 张锋 | 开源社翻译 Open-source projects have revolutionized the world of software development by fostering innovation, collaboration, and community-driven contributions. These projects are often the backbone of countl…

WebGL笔记:绘制多个点,三角形,以及画各种不同的线条,面

绘制多点 1 &#xff09; WebGL 缓冲区 我们在用js定点位的时候&#xff0c;肯定是要建立一份顶点数据的&#xff0c;这份顶点数据是给着色器的&#xff0c;因为着色器需要这份顶点数据绘图然而&#xff0c;我们在js中建立顶点数据&#xff0c;着色器肯定是拿不到的&#xff…

医疗小程序开发:技术门槛高?

随着移动互联网的普及&#xff0c;医疗行业也逐渐转向线上。医疗小程序开发成为了很多企业和医疗机构关注的焦点。但是&#xff0c;对于一些技术小白来说&#xff0c;可能会觉得医疗小程序开发技术门槛高&#xff0c;无从下手。实际上&#xff0c;使用乔拓云平台进入后台&#…

[尚硅谷React笔记]——第2章 React面向组件编程

目录&#xff1a; 基本理解和使用&#xff1a; 使用React开发者工具调试函数式组件复习类的基本知识类式组件组件三大核心属性1: state 复习类中方法this指向&#xff1a; 复习bind函数&#xff1a;解决changeWeather中this指向问题&#xff1a;一般写法&#xff1a;state.htm…

leetcode1610. 可见点的最大数目(java)

可见点的最大数目 题目描述滑动窗口 题目描述 难度 - 困难 leetcode1610. 可见点的最大数目 给你一个点数组 points 和一个表示角度的整数 angle &#xff0c;你的位置是 location &#xff0c;其中 location [posx, posy] 且 points[i] [xi, yi] 都表示 X-Y 平面上的整数坐标…

原子核的基本性质与放射性

原子核的基本性质与放射性 LaTeX 完整代码 \documentclass{article} \usepackage{fancyhdr} % 自定义页面的页眉和页脚样式 \usepackage{tocloft} % 控制目录&#xff08;包括目录、表格目录和插图目录&#xff09;样式的命令 \usepackage{titlesec} % 自定义标题的样式&…

学会安装Redis数据库到服务器或计算机(Windows版)

Redis 是一个基于内存的开源数据库系统&#xff0c;被广泛应用于 Web 应用、消息队列、缓存、实时统计等领域。它支持多种数据结构&#xff0c;包括字符串、哈希表、列表、集合、有序集合等&#xff0c;并提供了多种操作命令。 Redis 的特点如下&#xff1a; 内存存储&#xf…

【KingFusion】如何在3D场景实现流水效果

哈喽&#xff0c;大家好,我是雷工&#xff01; 在项目过程中&#xff0c;经常会涉及到实现管道水流动效果&#xff0c;此篇记录在KingFusion中的3D场景实现水流效果。 以下为简单流水效果的样例&#xff0c; 一、效果展示 当点击水泵&#xff0c;水泵启动&#xff0c;显示流水…

简单三步 用GPT-4和Gamma自动生成PPT PDF

1. 用GPT-4 生产PPT内容 我想把下面的文章做成PPT&#xff0c;请你给出详细的大纲和内容 用于谋生的知识&#xff0c;学生主要工作是学习&#xff0c;成年人的工作是养家糊口&#xff0c;这是基本的要求&#xff0c;在这之上&#xff0c;才能有更高的追求。 不要短期期望过高…

【数据结构】【C++】哈希表的模拟实现(哈希桶)

【数据结构】&&【C】哈希表的模拟实现(哈希桶&#xff09; 一.哈希桶概念二.哈希桶模拟实现①.哈希结点的定义②.数据类型适配③.哈希表的插入④.哈希表的查找⑤.哈希表的删除⑥.哈希表的析构 三.完整代码 一.哈希桶概念 哈希桶这种形式的方法本质上是开散列法&#x…

天选之子Linux是如何发展起来的?为何对全球IT行业的影响如此之大?

天选之子Linux是如何发展起来的&#xff1f;为何对全球IT行业的影响如此之大&#xff1f; 前言一、UNIX发展史二、Linux发展历史三、开源四、官网五、 企业应用现状六、发行版本 前言 上面这副图是博主历时半小时完成的&#xff0c;给出了Linxu的一些发展背景。球球给位看官老…

Java获取给定月份的前N个月份和前N个季度

描述&#xff1a; 在项目开发过程中&#xff0c;遇到这样一个需求&#xff0c;即&#xff1a;给定某一月份&#xff0c;得到该月份前面的几个月份以及前面的几个季度。例如&#xff1a;给定2023-09&#xff0c;获取该月份前面的前3个月&#xff0c;即2023-08、2023-07、2023-0…