【C++】map 与 set 的介绍与使用、力扣:692. 前K个高频单词

news2024/11/15 13:21:57

目录

一、关联式容器

二、键值对

三、set

3.1 set 的介绍

3.2 set 的使用

3.3. set 的使用举例

四、map

4.1 map的介绍

3.2 map 的使用

4.3 map的使用举例

五、经典练习题

1.set的使用

2.map的使用

思路一(稳定排序):

思路二(priority_queue):


一、关联式容器

在之前,我们接触过 vector、list、deque……,这些容器被称为序列式容器,因为其底层为线性序列的数据结构,里面存储的是元素本身。那么什么是关联式容器呢?

关联式容器:关联式容器也是用来存储数据的,与序列式容器不同的是,其中存储的是<key,value>结构的键值对,在数据检索时比序列式容器效率更高。

二、键值对

用来表示具有一一对应关系的一种结构,该结构中一般只包含两个成员变量 key 、value。

key代表键值,value 表示与 key 对应的信息。比如:现在要建立一个英汉互译的字典,那该字典必然有英文单词与其对应的中文含义,而且,英文单词与其中文含义是一一对应的关系,即通过该英文单词,在字典中就可以找到其对应的中文含义。

SGI-STL中关于键值对的定义:

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)
    {}
};

三、set

3.1 set 的介绍

这里是 set 的文档介绍:set的介绍_C++reference 以下是几个重要点。 

  1. set 是按照一定次序存储的容器
  2. 在 set 中,元素的 value 也标识它(value 就是 Key,类型为 T),并且每个 value 必须是唯一的。set中的元素不能在容器中修改(元素总是const),但是可以从容器中插入或删除它们。
  3. 在内部,set 中的元素总是按照其内部比较对象(类型比较)所指示的特定严格弱排序准则进行排序。
  4. set 容器通过 key 访问单个元素的速度通常比 unordered_set 容易慢,但它们允许根据顺序对子集进行直接迭代。
  5. set 在底层是用二叉搜索树(红黑树)实现的。

3.2 set 的使用

1. set 的模板参数列表

T:set 中存放元素的类型,实际在底层存储<value,value>的键值对。

Compare:set 中元素默认按照小于来比较。

Alloc:set 中元素空间的管理方式,使用STL提供的空间配置器管理。

2. set 的构造函数

 3. set 的迭代器

iterator begin() 返回 set 中起始位置的迭代器
iterator end()返回 set 中最后一个元素的迭代器
iterator rbegin()返回第一个元素的迭代器,即end()
iterator rend()返回最后一个元素的迭代器,即begin()

4. set 的修改操作

函数声明功能介绍
pair<iterator,bool> insert (const value_type& x)

在set中插入元素x,实际是插入<x,x>构成的键值对,如果插入成功,返回<该元素在set中的位置,true>,如果插入失败,说明x在set中已经存在,返回<x在set中的位置,false>

void erase ( iterator position )
删除 set position 位置上的元素
size_type erase ( const key_type& x )
删除 set 中值为 x 的元素,返回删除的元素的个数
void erase ( iterator fifirst, iterator last )
删除 set [first, last) 区间中的元素
void clear ( )
set 中的元素清空
iterator find ( const key_type& x ) const
返回set中值为x的元素的位置,不存在返回end()
size_type count ( const key_type& x ) const
返回set中值为x的元素个数(multiset中使用)

3.3. set 的使用举例

四、map

4.1 map的介绍

map的文档简介 以下是几个重要点:

  1. map是关联容器,它按照特定的次序(按照key来比较)存储由键值key和值value组合而成的元素。
  2. 在map中,键值key通常用于排序和唯一的标识元素,而值value中存储与此key关联的内容。简直key和值value的类型可能不同,并且在map的内部,key与value通过成员类型value_type绑定在一起,为其取名别称为pair
  3. 在内部,map中的元素总是按照及那只key进行排序的。
  4. map中通过简直访问但各国元素的熟读通常比unordered_map容器慢,但map允许根据顺序对元素进行直接迭代(即对map中的元素进行迭代时,可以得到一个有序的序列)
  5. map支持下标访问操作符,即在[]中放入key,就饿可以找到与key对应的value。
  6. map通常被实现为二叉搜索树(准确的说是红黑树)

3.2 map 的使用

1.map的模板参数说明

  •  key:键值对中key的类型
  • T:键值对中value的类型
  • Compare:比较器的类型。默认为less,如果存在特殊的比较,需要用户自己显示传入比较规则(函数指针或仿函数进行传递)

2.map的构造函数

 3.map的迭代器

iterator begin() 返回 map 中起始位置的迭代器
iterator end()返回 map 中最后一个元素的迭代器
iterator rbegin()返回第一个元素的迭代器,即end()
iterator rend()返回最后一个元素的迭代器,即begin()

4.insert

首先我们来看看insert的使用,首先要知道map的insert是插入pair类型的数据

 然后我们举例进行插入一下

void test_map1()
{
	//创建字典
	map<string, string> dict1;
	map<string, string> dict2;
	map<string, string> dict3;
	//插入数据——方式1、
	pair<string, string> kv1("sort", "排序");
	pair<string, string> kv2("insert", "插入");
	dict1.insert(kv1);
	dict1.insert(kv2);

	//插入数据——方式2、匿名对象
	dict2.insert(pair<string, string>("sort", "排序"));
	dict2.insert(pair<string, string>("insert", "插入"));

	//插入数据——方式3、make_pair()
	dict3.insert(make_pair("sort", "排序"));
	dict3.insert(make_pair("insert", "插入"));
}

插入方式3,make_pair其实是一个模板函数,就是为了方便我们进行pair结构的插入数据。

4. 下标访问操作符

这里我们实现一个统计水果次数功能的map,如下:

 但是这样的插入方式,非常的麻烦,所以map将下标访问操作符进行了重载,让其插入数据变得十分轻松。以下是 map中下标访问操作符:

功能解析:

  1. map中有这个key,返回value的引用。(查找、修改value)
  2. map中没有这个key,会插入pair(key,V()),并返回value的引用。(插入+修改)

其实下标访问操作符的本质是调用了 insert 函数,下面的文档中的实现方式:

这种方式其实不是太好理解,这里我们可以将其分为两步,就非常好理解了。

4.3 map的使用举例

因为返回的是其value的引用,所以我们可以对其进行赋值,这也是一种插入方式,也是最简单的一种插入方式。

 所以,在上面实现统计水果次数的map中,我们可以将其插入方式改为 [] 插入:

五、经典练习题

1.set的使用

题目链接:349. 两个数组的交集

题目介绍:

算法思路1:

  1. 因为是求交集,所以我们可以使用set对nums1、nums2进行排序+去重。
  2. 遍历set1,判断set1中的值是否存在于set2中,如果存在,则放到结果数组中,不存在则跳过(时间复杂度:O(N*N))。

算法思路2:

  1. 因为是求交集,所以我们可以使用set对nums1、nums2进行排序+去重。
  2. 同时处理两个区间,让it1指向的值与it2指向的值进行比较,如果相等则为交集,两个迭代器同时向后移动,如果不相等,结果小的迭代器进行向后移动(时间复杂度:O(N))

两种算法就是找交集的代码不同,这里一并给出源代码:

class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
        set<int> s1;
        set<int> s2;
        //去重  
        for(auto& e: nums1)
            s1.insert(e);
            
        for(auto& e: nums2)
            s2.insert(e);
        //找交集
        vector <int >result;
        //方法1:
        for(auto& e: s1)
        {
            if (s2.find(e)!=s2.end())
                result.push_back(e);
        }
        //============================================
        //方法2:
        set<int>::iterator it1=s1.begin();
        set<int>::iterator it2=s2.begin();
        while(it1!=s1.end()&&it2!=s2.end())
        {
            if (*it1==*it2)
            {
                result.push_back(*it1);
                it1++,it2++;
            }
            else if (*it1>*it2) it2++;
            else it1++;
        }
        return result;
    }
};

2.map的使用

题目链接:692. 前K个高频单词

题目介绍:

这题的难点就在于出现频率相等的情况下,要按照字典序排序,这就意味着不能简单的使用一次排序解决。

思路一(稳定排序):

算法思路:

  1. 使用map根据其字典序进行排序,并统计单词的出现次数。
  2. 通过sort根据单词的出现次数进行排序,然后控制其稳定性(次数相同则不交换)

使用了map存储后,单词在map中都按出现次数进行排序,但是此时,如果我们使用一种稳定排序,让其在符合次数相同时,并进行字典序的排序,就可以完成最终的排序。

首先 sort 是一个的迭代器是随机迭代器(RandomAccess Iterator),而map双向迭代器(bidirectional iterator),所以我们要先将map中的数据放入到一个数组中。

 因为sort是快速排序,不稳定的排序,所以我们要编写仿函数控制其排序的结果。

 仿函数的比较规则:

  1. 如果出现次数多,则排在前面,返回true。
  2. 出现次数相同时再比较字典序,如果kv2的字典序小于kv1,则也返回true,交换。
  3. 其他情况直接返回false。
class Solution {
public:
    struct Greater{
        bool operator()(const pair<string,int>& kv1,const pair<string,int>& kv2)
        {
            //按次数比较
            if (kv1.second > kv2.second)
                return true;
            //次数相同,按字典序比较。
            if (kv1.second==kv2.second&&kv1.first < kv2.first)
                return true;
            return false;
        }
    };
    vector<string> topKFrequent(vector<string>& words, int k) {
        map<string,int> countMap;
        for(auto& str:words)
        {
            countMap[str]++;
        }
        //导数据,因为迭代器类型不同
        vector<pair<string,int>> sortV(countMap.begin(),countMap.end());
        sort(sortV.begin(),sortV.end(),Greater());

        vector <string> result;
        for(int i=0;i<k;i++)
        {
            result.push_back(sortV[i].first);
        }
        return result;
    }
};

同样,我们可以直接使用库中的稳定排序(stable_sort)

 进行以下修改即可:

 

思路二(priority_queue):

算法思路:

  1. 使用map根据其字典序进行排序,并统计单词的出现次数。
  2. 再将数据放入priority_queue中,通过自定义仿函数建立k的数的小堆。
  3. 再将前k个数放入结果数组中。

既然要使用我们自己的仿函数,则模板参数这我们要额外注意,我们要按顺序传入参数,在仿函数前要先传入存储容器,因为此题使用的vector进行的存储,所以直接传入vector即可。

注意,第三个仿函数参数我们直接传入仿函数名即可,因为我们自己实现的仿函数一般不会再设置模板。不要写浑了(hhh)。

 仿函数的比较规则:

 TOP-K问题要建小堆,(如果建立大堆,只能求出最大的值,其他比它小的值进不了堆)。

  1. value小则返回true。
  2. value相同时再比较字典序,字典序如果kv1大于kv2,则返回true。
  3. 其他情况直接返回false。
struct Less {
	bool operator()(const pair<string, int>& kv1, const pair<string, int>& kv2)
	{
		//按次数比较
		if (kv1.second < kv2.second)
			return true;
		//次数相同,按字典序比较。
		if (kv1.second == kv2.second && kv1.first > kv2.first)
			return true;
		return false;
	}
};

然后我们将数据插入到priority_queue中,我们可以使用迭代器区间进行初始化,也可以直接push插入数据。

因为要返回前k的数据。所以使用while循环,在priority_queue中取出前k个top数据,将其pair中的first放入结果数组中。

class Solution {
public:
    struct Less{
        bool operator()(const pair<string,int>& kv1,const pair<string,int>& kv2)
        {
            //按次数比较
            if (kv1.second<kv2.second)
                return true;
            //次数相同,按字典序比较。
            if (kv1.second==kv2.second&&kv1.first>kv2.first)
                return true;
            return false;
        }
    };
    vector<string> topKFrequent(vector<string>& words, int k) {
        map<string,int> countMap;
        for(auto& str:words)
        {
            countMap[str]++;
        }
        //迭代器区间初始化
        priority_queue <pair<string,int>,vector<pair<string,int>>,Less> 
        maxHeap(countMap.begin(),countMap.end());
        vector<string> result;
        while(k--)
        {
            result.push_back(maxHeap.top().first);
            maxHeap.pop();
        }
        return result;
    }
};

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

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

相关文章

jvm参数说明

-Xmx3550m&#xff1a;设置JVM最大堆内存为3550M。 -Xms3550m&#xff1a;设置JVM初始堆内存为3550M。此值可以设置与-Xmx相同&#xff0c;以避免每次垃圾回收完成后JVM重新分配内存。 -Xss128k&#xff1a;设置每个线程的栈大小。JDK5.0以后每个线程栈大小为1M&#xff0c;之…

新式 AIMD 拥塞控制

周三晚上发了一则朋友圈&#xff0c;今天整理一下&#xff1a; ​ AIMD 过程可推导出 TCP 吞吐公式&#xff1a; TaRTTbpT\dfrac{a}{RTT}\dfrac{b}{\sqrt{p}}TRTTa​p​b​ a&#xff0c;b 分别为与 AIMD 参数和过程有关&#xff0c;该公式结论内置公平性。设 MSS 1460&…

【MySQL】九,MySQL逻辑架构剖析

服务器处理客户端请求 Connectors 外部的各种类型的连接客户端。 第一层&#xff1a;连接层 客户端访问 MySQL 服务器前&#xff0c;首先要建立 TCP 连接。 经过三次握手成功建立连接后&#xff0c;MySQL对传输过来的账号密码做身份认证、权限获取。 如果用户名密码错误&am…

【Iava】抽象类和接口

祝早日驱散阴霾 文章目录1. 抽象类1.1 抽象类的概念1.2 抽象类的使用注意事项1.3 抽象类的作用2. 接口2.1 接口的定义2.2 接口的使用规则2.3 创建对象拷贝,实现Cloneable3. 抽象类和接口的区别1. 抽象类 1.1 抽象类的概念 抽象类,顾名思义,这是不是一个具体的类,没有实际的功…

四旋翼无人机学习第17节--封装上传github与gitee

0 前言 经过一段时间的绘制&#xff0c;终于绘制完项目所需的封装。为了更好的管理封装&#xff0c;我准备把封装与焊盘上传到github与gitee&#xff0c;方便之后的管理工作。 1 文件上传gitee 注册这里就不作介绍咯。 1、首先点击新建仓库。 2、设置仓库的名称与项目介绍&…

GO语言初始化数据结构的方法你知道吗?

xdm 我是阿兵云原生 使用 golang 进行编码过程中&#xff0c;你们都是如何初始化数据结构的呢 GO 里面提供了 2 个关键字来进行初始化数据结构 newmake 不过有时候我也会直接使用花括号的方式直接就初始化&#xff0c;例如这样&#xff1a; name : []string{"xiaomot…

电源PCB布局要点

本次已常用的集成电源IC芯片TPS 5430.讲解一下布局要求。 在手册中可以看到官方已给出的详细布局。 这是我们原理图设计的TPS 5430。 通过以上了解到了连接关系以及布局要求。那么我们再看看官方要求是如何硬性要求布局的。 总结&#xff1a; 1.输入电容要靠近Vin 管脚 2.电…

图像简单运算

图像运算是以图像为单位对图像进行数学操作&#xff0c;运算对象以像素点为基本单位&#xff0c;运算结果为一幅灰度分布与原图像不同的新图像。 算术运算与逻辑运算 算术运算与逻辑运算中每次只涉及一个空间像素的位置&#xff0c;所以可以”原地操作“&#xff08;在&#xf…

【微服务笔记02】微服务组件之Eureka常见的几个配置属性及其常见的几个概念

这篇文章&#xff0c;主要介绍微服务组件中Eureka常见的几个配置属性及其常见的几个概念【源代码地址】。 目录 一、Eureka常见配置属性 1.1、instance配置项 1.2、client配置项 1.3、server配置项 二、eureka常见概念 2.1、服务注册 2.2、服务发现 2.3、服务续约 2.4…

CSS常见标签知识回顾

学前端的一点css笔记&#xff0c;参考的是黑马程序员pink老师的教程。 一.行内元素 1.一行可以显示多个 2.宽度核高度默认由内容撑开 3.不可以设置宽高 代表标签&#xff1a; a、span、b、ui、s、strong、ins、em、del 二、行内块元素 1.一行可以显示多个 2.可以设置宽高…

回顾2022年总结

文章目录前言这一年感悟感想成绩新的一年期许前言 2022过的好快&#xff0c;接着我对自己这一年的所做的事情来进行一个汇总和感悟感想总结&#xff01; 所有博客文件目录索引&#xff1a;博客目录索引(持续更新) 这一年 月份做了什么事1转本备考、Java技术(netty一套课程、…

VisionMaster应用实例——识别锁付孔位

项目背景&#xff1a;相机对法兰盘表面进行拍照&#xff0c;获取法兰盘上16个孔位的位置&#xff0c;通过VisionMaster进行TCP通信&#xff0c;将位置信息发送给机械臂&#xff0c;对这16孔位的螺丝进行拧紧&#xff0c;同时需要根据十字锁付的特点。 思路分析&#xff1a; 1.方…

[oeasy]python0036_牛说_cowsay_小动物说话_asciiart_figlet_lolcat_管道(祝大家新年快乐~)

牛说(cowsay) 回忆上次内容 上次我们研究了shell脚本的编程 并且在shell中实现了 循环语句延迟命令清屏命令python命令figlet命令还能整点什么呢&#xff1f;&#x1f914;还想要让小动物报时 cowsay 首先要安装 cowsay sudo apt install cowsay 装完之后 cow 就可以 say 了…

人工智能-逻辑回归

1&#xff0c;逻辑回归介绍 逻辑回归&#xff1a;分类模型 应用场景&#xff1a;广告点击率、是否垃圾邮件 逻辑回归主要用于解决二分类问题 输入&#xff1a; 逻辑回归的输入&#xff1a;线性回归的结果 激活函数&#xff1a;sigmoid函数 回归的结果输入到sigmoid函数中 输…

UE5笔记【十四】漂流者游戏制作【一】地形材质准备

针对游戏的剧情设计&#xff1a;角色流落荒岛&#xff0c;寻找食物和水&#xff0c;等待救援。只有按时找到才能存活。否则游戏失败了。 新建一个第三人称游戏。可以将项目名称命名为《CastAway》 启动Landmass插件 然后启用Water插件。 然后删除场景中不需要的东西。保留荒岛…

DoIP协议从入门到精通——诊断通信

DoIP(Diagnostic on IP)是基于IP(车载通信总线介质)为实现诊断诊断通信搭建通信桥梁的过程。从车辆连接-车辆声明-激活通信,都在前面几篇文章有过分享: 在专栏中可以找到文章! 如下述将DoIP协议内容映射到计算机OSI七层通信模型中,其作用就是将物理层接收到的收据,准…

(十) Docker微服务实战

Docker微服务实战一、IDEA新建一个普通微服务模块二、通过dockerfile发布微服务部署到docker容器2.1、打包模块2.2、编写 Dockerfile以及上传jar包2.3、构建镜像2.4、运行容器一、IDEA新建一个普通微服务模块 建Module docker_boot Pom文件 <?xml version"1.0" e…

2022年技术自媒体经验分享 —— 开始尝试认真做 B站 的一年后的复盘

文章目录前言一、回顾过去1、进驻B站2、进驻知识星球3、做全网最强的算法星球4、付费是道砍二、星球简介1、星主介绍2、星球简介三、星球内容1、九日集训2、付费专栏 - 免费阅读3、每月算法集训4、每日作业5、个人规划6、更优质的朋友圈7、向我提问 - 深思熟虑你的问题8、持续创…

微机原理真题错题整理2018,2019年

目录 2017年 选择 填空题 2018年 选择 填空题 编程题 2017年 选择 1:若一SRAM芯片的容量是10248位&#xff0c;除电源和接地外该芯片的其他引脚最少应为&#xff08;&#xff09; A:23 B:22 C:21 D:20 10242^1010根地址线 8位8根数据线 …

【SV】为什么需要异步复位?同步输入/异步输入、异步复位/同步复位(bad)、异步复位同步释放、同步系统/异步系统

【SV】为什么需要异步复位&#xff1f;同步输入/异步输入、异步复位/同步复位&#xff08;bad&#xff09;、同步系统/异步系统 1. 同步输入/异步输入&#xff08;synchronous and asynchronous inputs in sequential system&#xff09;为什么需要异步复位&#xff1f;1.1 异步…