【C++】unordered_map和unordered_set的使用 及 OJ练习

news2024/10/6 6:39:58

文章目录

  • 前言
  • 1. unordered系列关联式容器
  • 2. map、set系列容器和unordered_map、unordered_set系列容器的区别
  • 3. unordered_map和unordered_set的使用
  • 4. set与unordered_set性能对比
  • 5. OJ练习
    • 5.1 在长度 2N 的数组中找出重复 N 次的元素
      • 思路分析
      • AC代码
    • 5.2 两个数组的交集
      • 思路分析
      • AC代码
    • 5.3 两个数组的交集 II
      • 思路分析
      • AC代码
    • 5.4 存在重复元素
      • 思路分析
      • AC代码
    • 5.5 两句话中的不常见单词
      • 思路分析
      • AC代码

前言

在前面的文章中,我们已经学习了STL中底层为红黑树结构的一系列关联式容器——set/multiset 和 map/multimap(C++98)

1. unordered系列关联式容器

在C++98中,STL提供了底层为红黑树结构的一系列关联式容器,在查询时效率可达到 l o g 2 N log_2 N log2N,即最差情况下需要比较红黑树的高度次。
在C++11中,STL又提供了4个unordered系列的关联式容器,这四个容器与红黑树结构的关联式容器使用方式基本一样,只是其底层结构不同。
本文中只对unordered_map和unordered_set进行介绍,
unordered_multimap和unordered_multiset大家可自行查看文档介绍。

2. map、set系列容器和unordered_map、unordered_set系列容器的区别

首先我们来简单说一下前面学的不带unordered的几个容器和这篇文章学习的unordered系列的容器有什么区别。

首先,它们的底层结构是不一样的:

我们前面学习的那一系列关联式容器——set/multiset 和 map/multimap它们的底层结构是红黑树,而我们这篇文章要学的unordered系列的——unordered_map/unordered_multimap、unordered_set/unordered_multiset它们的底层是哈希表,至于什么是哈希表,大家有的可能听说过,有的可能没有,没关系,我们后面会讲。
同样的,unordered系列中,带multi的和不带multi的区别也是允许键值重复出现和不允许重复出现的问题。

其次,从名字上我们其实就能得出它们的第二个区别:

unordered不就是无序的意思嘛。
所以,map和set我们用迭代器遍历,得到的是有序的序列,二unordered系列,我们去遍历的话,得到的是无序的。
其实单从使用上来说最大的区别就是这个。

那说到迭代器,它们的迭代器也是有区别的:

map和set系列它们的迭代器是双向迭代器,而unordered系列它们的迭代器是单向迭代器。

3. unordered_map和unordered_set的使用

其实单从使用来说,大家如果学会了我们之前讲的C++98的那几个关联式容器——set/multiset 和 map/multimap的使用的话,那C++11的这4个unordered系列的关联式容器其实大家就直接可以用了,因为它们的用法基本一致,常用的接口都差不多。

所以下面我们就简单介绍一下它们的使用,然后做一些练习,另外还有一些东西是需要我们学了它们的底层才能看懂的,这篇文章我们也先不做讲解。

首先我们可以看一下unordered_map的接口:

在这里插入图片描述
常用的接口还是那几个,跟map的用法一样,还有一些看不懂的,我们现在不用管,那些是跟他的底层结构哈希有关的。
另外我们会注意到它的迭代器没有rbegin、rend,因为它的迭代器是单向的嘛,都不支持反向遍历。

然后unordered_set我们也可以简单看一下:

在这里插入图片描述
接口也都差不多,只是set系列的没有[]和at接口

还是给大家简单演示一下它的使用吧:

在这里插入图片描述
这使用起来是不是跟set差不多啊,只不过我们看到它这里遍历是无序的。
当然也可以用迭代器遍历。
我们可以跟set对比一下
在这里插入图片描述

那unordered_map,也简单演示一下:

我们可以用unordered_map来跑一下那个统计次数的程序:
在这里插入图片描述
同样我们可以和map对比一下
在这里插入图片描述
其实还是有序无序的区别,只不过这里按照key排序,我们的key是汉字(水果名称),所以不太好看排序的效果。
当然这种场景的话其实顺序也不重要了。

那大家思考一下,既然它们好像跟map和set差不多,那为什么还要提高unordered系列呢?有什么优势吗?

其实在文档里面也有一些说明
比如我们看unordered_map
在这里插入图片描述
🆗,由于它底层使用的哈希结构,使得它们能够更快的按照键值去访问某个元素。

4. set与unordered_set性能对比

那我这里呢也提供了一段代码,以set和unordered_set为例来测试对比一下它们的性能:

因为unordered系列和非unordered系列它们底层的数据结构都是一样的,所以我们这里拿一组去对比就可以了

先看一下代码吧:

int main()
{
	const size_t N = 1000000;

	unordered_set<int> us;
	set<int> s;

	vector<int> v;
	v.reserve(N);
	srand((unsigned int)time(nullptr));
	for (size_t i = 0; i < N; ++i)
	{
		v.push_back(rand());
		//v.push_back(rand()+i);
		//v.push_back(i);
	}

	size_t begin1 = clock();
	for (auto e : v)
	{
		s.insert(e);
	}
	size_t end1 = clock();
	cout << "set insert:" << end1 - begin1 << endl;

	size_t begin2 = clock();
	for (auto e : v)
	{
		us.insert(e);
	}
	size_t end2 = clock();
	cout << "unordered_set insert:" << end2 - begin2 << endl;


	size_t begin3 = clock();
	for (auto e : v)
	{
		s.find(e);
	}
	size_t end3 = clock();
	cout << "set find:" << end3 - begin3 << endl;

	size_t begin4 = clock();
	for (auto e : v)
	{
		us.find(e);
	}
	size_t end4 = clock();
	cout << "unordered_set find:" << end4 - begin4 << endl << endl;

	cout << s.size() << endl;
	cout << us.size() << endl << endl;;

	size_t begin5 = clock();
	for (auto e : v)
	{
		s.erase(e);
	}
	size_t end5 = clock();
	cout << "set erase:" << end5 - begin5 << endl;

	size_t begin6 = clock();
	for (auto e : v)
	{
		us.erase(e);
	}
	size_t end6 = clock();
	cout << "unordered_set erase:" << end6 - begin6 << endl << endl;

	return 0;
}

简单解释一下这段代码:

其实就是搞了一个set和一个unordered_set,然后我们去控制产生一些随机数,先放到一个vector里面,再分别插入到set和一个unordered_set里面,对比它们插入、查找、删除的性能。
插入之后我们还统计了一下实际插入的个数,因为rand函数产生的随机数是有限的。

我们来测试几组:

先来10万个随机数
在这里插入图片描述
我们可以看到unordered_set三种操作都是比较快的,但是大家看到虽然我们产生10万个随机数,但是实际插入只有3万多个,因为rand产生的随机数会有大量重复值。

如果想减少数据有大量重复,可以用这个:

在这里插入图片描述
对每次产生的随机数加一个i,因为i每次是不同的嘛,这样重复数据肯定会减少
运行一下
在这里插入图片描述
大家自己对比一下

当然我们可以插入i,这样就没有重复值了

在这里插入图片描述

所以:

综合而言,unordered系列的效率是比较高的,尤其是find的效率(因为它底层的哈希结构,这个我们后面会讲)。
当然大家不要太关注我们上面的测试结果,因为可能在某些特定场景下unordered系列的插入删除这样操作不一定有map/set快(比如如果一直插入有序数据的话,set底层红黑树就会一直向一边旋转,最终就会比较平衡,那它的插入删除就不一定比unordered差了),但它的查找一定是很快的。
所以我们说的是综合各种场景而言,unordered系列的综合性能是较好的。

5. OJ练习

下面我们来做几道相关的OJ题

5.1 在长度 2N 的数组中找出重复 N 次的元素

题目链接: link
在这里插入图片描述

思路分析

这道题给我们一个长度为2n的数组,其中有一个元素恰好出现n次,我们要找到并返回这个元素。
那我们是不是统计出次数就好办了,统计出次数然后找到次数为n的返回就行了,那统计次数的话我们就可以用unordered_map(当然map也可以)。

AC代码

写一下代码:

在这里插入图片描述
在这里插入图片描述

class Solution {
public:
    int repeatedNTimes(vector<int>& nums) {
        int n=nums.size()/2;
    
        unordered_map<int,int> m;
        for(auto e:nums)
        {
            m[e]++;
        }

        for(auto e:m)
        {
            if(e.second==n)
                return e.first;
        }
        return -1;
    }
};

5.2 两个数组的交集

题目链接: link

在这里插入图片描述
这道题我们是不是前面刚做过啊,当时我们用set去搞的,set达到一个排序+去重的效果,然后就好办了(具体解法大家可以去看之前文章的讲解)。

那我们今天学的是unordered_set,它不会进行排序,那我们要怎么解决?

思路分析

那这道题其实只用unordered_set也能搞:

unordered_set虽然不能排序,但是也是可以去重的,首先我们先对两个数组进行去重。
然后,我们遍历其中一个数组,遍历的同时去依次判断当前元素在不在另一个数组中,如果在,就是交集。

AC代码

在这里插入图片描述
在这里插入图片描述

class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
        unordered_set<int> s1(nums1.begin(),nums1.end());
        unordered_set<int> s2(nums2.begin(),nums2.end());
        vector<int> ret;

        for(auto e:s1)
        {
            if(s2.find(e)!=s2.end())
            {
                ret.push_back(e);
            }
        }

        return ret;
    }
};

5.3 两个数组的交集 II

题目链接: link
在这里插入图片描述
来看这道题,是上一题的一个升级版,还是求交集,但是多了一些要去:

返回结果中每个元素出现的次数,应与元素在两个数组中都出现的次数一致(如果在两数组中出现次数不一致,则考虑取较小值)。
但是它没有要去输出结果中每个元素是唯一的。

怎么搞?

思路分析

那这道题的关键其实在于控制这个次数:

最终返回的交集中,每个元素出现的次数要和它们在两个数组中出现的次数一样,如果在两个数组中出现次数不一样,则取较小值。

所以我们可以这样搞:

用unordered_map(当然map也可以)先统计出一个数组每个元素的个数。
然后遍历第二个数组,依次取每个元素判断其是否在map中存在等效键(用count接口),如果存在就是交集,放入vector里面并让其对应的次数–,如果次数减到0了,就从map中删除掉,因为此时它的个数已经等于它在两数组中出现次数的较小值了。
如果不删除,后面在第二个数组中再遇到的话,次数就会超。

如果但看思路不太理解的话可以结合下面的代码看。

AC代码

在这里插入图片描述
在这里插入图片描述

class Solution {
public:
    vector<int> intersect(vector<int>& nums1, vector<int>& nums2) {
        unordered_map<int,int> m;
        vector<int> ret;
        for(auto e:nums1)
        {
            m[e]++;
        }

        for(auto e:nums2)
        {
            if(m.count(e))
            {
                ret.push_back(e);
                m[e]--;
                
                if(m[e]==0)
                {
                    m.erase(e);
                }
            }
        }

        return ret;
    }
};

5.4 存在重复元素

题目链接: link
在这里插入图片描述
这道题给我们一个数组,如果存在任意一个值出现至少两次,就返回true,否则返回false。

思路分析

那这个太简单了,统计一下次数,判断有没有次数大于等于2的就行了

AC代码

class Solution {
public:
    bool containsDuplicate(vector<int>& nums) {
        unordered_map<int,int> m;
        for(auto e:nums)
        {
            m[e]++;
        }

        for(auto e:m)
        {
            if(e.second>=2)
                return true;
        }

        return false;
    }
};

在这里插入图片描述

5.5 两句话中的不常见单词

题目链接: link
在这里插入图片描述
这道题其实就是让我们找出在两句话中只出现一次的那些单词。

思路分析

那其实思路很简单:

只要统计出这两句话中每个单词出现的次数就行了,次数为1的就是要找到不常用单词。
而这道题麻烦的是他给我们的是两个字符串,所以我们要统计单词次数的话可以先按空格把单词分割出来,放到一个vector里面,这样比较好统计。

AC代码

class Solution {
public:
    vector<string> uncommonFromSentences(string s1, string s2) {
        //加个空格,把两句话合二为一
        string s=s1+' '+s2;

        //按空格拆分句子中的单词放到vector里面
        vector<string> v;
        string word;
        for(auto e:s)
        {
            if(e!=' ')
            {
                word+=e;
            }
            else
            {
                v.push_back(word);
                word.clear();
            }
        }
        //最后结束没有空格,所以要多push一次
        v.push_back(word);

        //统计次数
        unordered_map<string,int> m;
        vector<string> ret;

        for(auto e:v)
        {
            m[e]++;
        }

        //只出现一次的单词就是不常用单词
        for(auto e:m)
        {
            if(e.second==1)
            {
                ret.push_back(e.first);
            }
        }

        return ret;
    }
};

在这里插入图片描述

在这里插入图片描述

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

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

相关文章

matlab实现输出的几种方式(disp函数、fprintf函数、print函数)

matlab实现输出的几种方式&#xff08;disp函数、fprintf函数、print函数&#xff09; 输出为文本、文件、打印 1、disp函数 显示变量的值&#xff0c;如果变量包含空数组&#xff0c;则会返回 disp&#xff0c;但不显示任何内容。 矩阵 A [1 0]; disp(A)结果 字符串 S …

腾讯云-对象存储服务(COS)的使用总结

简介 对象存储&#xff08;Cloud Object Storage&#xff0c;COS&#xff09;是腾讯云提供的一种存储海量文件的分布式存储服务&#xff0c;具有高扩展性、低成本、可靠安全等优点。通过控制台、API、SDK 和工具等多样化方式&#xff0c;用户可简单、快速地接入 COS&#xff0…

多肽合成15266-88-3产品特点介绍,Cyclo(glycyl-L-histidyl)

中文名&#xff1a;环(甘氨酰-L-组氨酰) 英文名&#xff1a;cyclo(Gly-His) CYCLO(-GLY-HIS) Cyclo(glycyl-L-histidyl) (S)-3-(1H-Imidazol-4-ylmethyl)-2,5-piperazinedione CAS&#xff1a;15266-88-3 分子式&#xff1a;C8H10N4O2 分子量&#xff1a;194.191 密度 …

【点击新增一个下拉框 与前一个内容一样 但不能选同一个值】

点击新增一个下拉框 与前一个内容一样 但不能选同一个值 主要是看下拉选择el-option的disabled,注意不要混淆 <el-form label-width"120px" :model"form" ref"form" style"color: #fff"><template v-for"(trapolicy, i…

JAVA基础知识四——重载、可变参数

1、方法重载 注意事项&#xff0c;构成重载的必要 1.1、课堂练习——选择题 1.2、课堂练习——程序代码 public class Overload {//编写一个main方法。public static void main(String[] args){Methods method new Methods();/*method.m(10);method.m(10,11);method.m(&quo…

Blazor:Razor组件防止点击穿透

文章目录 微软官方文档如何防止点击传统 微软官方文档 如何防止点击传统 我们就算封装成立Blazor组件&#xff0c;我们还是对原生的DOM元素进行操作 在子组件最外层包一个div。在DIV里面阻止组件事件的传播 <div style"margin-left:10px;" onclick:stopPropaga…

drools8尝试(加单元测试)

drools8的maven模板项目里没有单元测试, 相比而言drools7有个非常好的test senorios 那就自己弄一个 文件是.http后缀的,写了个简单的例子如下 //测试交通违章 POST http://localhost:8080/Traffic Violation accept: application/json Content-Type: application/json{&q…

人工智能AI绘画接入使用文档

人工智能AI绘画接入使用 一、人工智能AI绘画二、使用步骤1、接口2、请求参数3、请求参数示例4、接口 返回示例 三、 AI绘画优秀描述例子四、 如何获取appKey和uid1、申请appKey:2、获取appKey和uid 五、重要说明六、AI绘画成果展示 一、人工智能AI绘画 AI作画,用户可以在平台上…

Python爬虫逆向实战案例(五)——YRX竞赛题第五题

题目&#xff1a;抓取全部5页直播间热度&#xff0c;计算前5名直播间热度的加和 地址&#xff1a;https://match.yuanrenxue.cn/match/5 cookie中m值分析 首先打开开发者工具进行抓包分析&#xff0c;从抓到的包来看&#xff0c;参数传递了查询参数m与f&#xff0c;同时页面中…

【实操干货】如何开始用Qt Widgets编程?(三)

Qt 是目前最先进、最完整的跨平台C开发工具。它不仅完全实现了一次编写&#xff0c;所有平台无差别运行&#xff0c;更提供了几乎所有开发过程中需要用到的工具。如今&#xff0c;Qt已被运用于超过70个行业、数千家企业&#xff0c;支持数百万设备及应用。 在本文中&#xff0…

Anaconda Conda实现Python多环境管理

Anaconda Conda实现Python多环境管理 Python多环境AnacondaConda环境管理下载安装镜像配置环境管理常用命令创建Python3.10环境 Python多环境 Python多环境指的是在同一台计算机上同时安装并管理多个不同的Python版本。可以在不同版本的Python之间切换&#xff0c;并确保每个项…

WPS office 最新未公开 0Day漏洞警示

一、事件描述 近日&#xff0c;网传监测发现WPS Office for Windows版本 存在0day漏洞&#xff0c;攻击者可以利用该0day漏洞在受害者主机上执行任意恶意文件&#xff0c;高危级别&#xff0c;官方尚未对此发布修复漏洞&#xff0c;目前建议只能临时弃用wps或者不要点开未知文件…

8月21-22日上课内容 第一章 MySQL数据库初始

本章结构 数据库的基本概念 概述&#xff08;总览&#xff09; 结构&#xff1a; 数据 表 数据库 数据库管理系统 数据库系统原理 数据 (Data) 描述事物的符号记录 包括数字&#xff0c;文字、图形、图像、声音、档案记录等以“记录”形式按统一的格式进行存储表 将不同…

ClickHouse安装及部署

文章目录 Docker快速安装Ubuntu预编译安装包安装检查是否支持SSE4.2使用预编译安装包 Tgz安装包配置文件修改修改密码配置远程访问 其他主机访问文章参考 Docker快速安装 本地pull镜像 docker run -d --name ch-server --ulimit nofile262144:262144 -p 9000:9000 -p 8123:81…

【数据结构与算法】1. 绪论

1. 绪论 1.1 数据结构 1.1.1 数据结构的基本概念 1.1.2 数据结构的三要素 数据结构三要素&#xff1a; 逻辑结构 划分方法一&#xff1a; 线性结构&#xff1a;线性表、栈、队列、串非线性结构&#xff1a;树、图 划分方法二&#xff1a; 集合结构线性结构树形结构网状&…

学Python静不下来,看了一堆资料还是很迷茫是为什么

一、前言 最近发现&#xff0c;身边很多的小伙伴学Python都会遇到一个问题&#xff0c;就是资料也看了很多&#xff0c;也花了很多时间去学习但还是很迷茫&#xff0c;时间长了又发现之前学的知识点很多都忘了&#xff0c;都萌生出了想半路放弃的想法。 让我们看看蚂蚁金服的大…

按钮权限控制

搜索关键字&#xff1a; 自定义指令传参| "自定义指令""dataset"|自定义指令dataset| "Vue""directives"|vue按钮权限实现 1、完整代码&#xff1a; <template> <div> <el-breadcrumb separator-class"el-icon…

CheckBox全选,半选,不选三种样式原生实现

效果图 代码 <!DOCTYPE html> <html> <head><title>复选框样式示例</title><style>input[type"checkbox"] {display: none; /* 隐藏原生复选框 */}label.checkbox {position: relative;display: inline-block;width: 20px;heig…

初出茅庐的小李博客之USB设备开发快速上手

1.USB基础知识介绍 这里有一篇文章写的非常好不再重复造轮子 USB基础知识介绍&#xff1a;https://blog.csdn.net/Richard_Brown/article/details/106602288 2.USB设备介绍 常见的USB设备&#xff1a;U盘、鼠标、MP3、移动硬盘、数码相机、键盘、游戏杆、USB摄像头、USB打印…

MVC OR DDD

MVC OR DDD 说明&#xff1a;这篇是标题党&#xff0c;不包含相关概念说明 前段时间跟随师兄学习了解了DDD领域驱动模型&#xff0c;觉得这个思想更好&#xff0c;进行下面解析和学习方面的思考和实践&#xff0c;觉得很好&#xff0c;耐心读下去。希望对您有所帮助。 首先&am…