关联式容器set和map

news2024/11/23 15:36:13

在这里插入图片描述

文章目录

    • 一.容器
    • 二.set的介绍
      • 1.insert
      • 2.lower_bound&&upper_bound
      • 3.find和count
        • find
        • count
    • 三. multiset
    • 四.map
        • 最特别的operator[]
    • 四.multimap,因为允许键值冗余,所以它没有operator[],它的find返回的是中序遍历第一次遇到的节点
    • 五.两个练习题

一.容器

在C++中容器大致可以分为两种,分别是:序列式容器和关联式容器。

序列式容器:vectorlist,deque,forward_lsit都是序列式容器,因为它们的底层都是线性序列的数据结构,存放的是元素本身。

关联式容器:虽然也是用来存储数据的,但是关联式容器中存放的并不是元素本身,而是<key,value>这样的键值对,这样的容器在数据检索的时候效率会更高(插入删除不需要挪动数据,只需要更改指针指向,结构平衡时查找效率为logN)。

关联式容器也有两类,一类是map,multimap和set,multiset这种底层为红黑树的容器,另一类是哈希结构。

二.set的介绍

在这里插入图片描述

set的底层是一棵搜索二叉树,搜索二叉树在构建的时候会自动排序,并且不能插入大小相同的值,如果你往树中插入大小相同的值,它会自动给你去重,所以set其实是去重+排序

在这里插入图片描述

set有一个模板参数T和一个仿函数以及空间配置器(STL中的容器为了减少扩容时的效率损失都是从内存池中开空间的),表面上set只有一个参数T,但其实set内部存放的是<value,value>这样的键值对

set的大部分成员函数和STL中其他的容器类似,所以就不一一介绍,这里只介绍具有set特性的成员函数

1.insert

在这里插入图片描述

第一个插入函数插入的参数是一个value_type的类型,其实这个类型是一个pair被typedef以后的名字
在这里插入图片描述

此外观察pair的参数,可以看到set中的key是不可以被修改的,而value是可以修改的。

第二个插入函数实在某个位置插入一个节点,但是这个接口要慎用,因为有可能会破坏到树的结构。


关于pair

在这里插入图片描述

pair是一个struct的模板类,里面有两个成员,通常我们将first认为是keysecond认为是value,但它们的类型具体是什么则由我们自己决定,,一般我们将pair称之为键值对,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)
	{}
};

以前我们定义搜索树时我们的KV结构是由两个变量来代表,map这里使用pair存储就是将两个变量替换成了一个pair模板类,这比使用两个变量来实现的KV结构可以解决更多的问题

2.lower_bound&&upper_bound

lower_bound返回大于等于目标值的迭代器,upper_bound返回大于目标值的迭代器,在set中用于返回目标值的迭代器。(可以将获取到的两个迭代器作为一个迭代器区间用于删除或插入)

在这里插入图片描述

可以看到这个erase将2和3都给删掉了,可以理解为删除的是一个这样的区间:[2,3]

3.find和count

find

在这里插入图片描述

find采用的是中序遍历的查找方式,如果找到了就返回这个节点的迭代器,如果没找到就返回set::end

count

在这里插入图片描述

给定一个值,该函数能帮你统计该树种拥有该值的节点有多少个。或许有人会说:set是排序+去重,一个值肯定就一个,这个接口函数是不是没有意义?

其实该函数并不是为了set而创建的,而是为了multiset才创建的。

三. multiset

在这里插入图片描述

multiset与set的不同就在于multiset允许键值冗余(可以存在相同的值),因此它只是简单的排序

在这里插入图片描述

但是因为multiset中会存在相同的值,所以有些接口在set中显得有些鸡肋,但在multiset种却刚刚好,比如count:

在这里插入图片描述

count可以统计multiset中某个值出现的次数

此外因为mulitset中允许键值冗余,所以它的find函数找到的是中序遍历中第一次出现的结果

在这里插入图片描述

四.map

map是一个平衡搜索二叉树,是KV模型,set虽然也是KV模型,但其实存放的是一个<value,value>的键值对,而map存放的是则是真正的<key,value>键值对

#include<iostream>
#include<map>
using namespace std;

int main()
{
	map<string, string>dict;
	dict.insert(pair<string, string>("一", "one"));
	dict.insert(pair<string, string>("二", "two"));//这里在调用pair的构造函数
	dict.insert(pair<string, string>("单词", "world"));
	dict.insert(make_pair("书", "book"));//这里和上面三个是等价的写法

	return 0;
}

这里为了简化,使用一个make_pair函数

在这里插入图片描述

make_pair其实也是在调用pair的构造函数,但它的好处就是它是一个函数模板,可以自动推演,不用我们显示去声明类型。

大部分情况插入键值都是使用make_pair

map的迭代器和list的十分类似,都是通过在类中内嵌一个指针来实现的,所以这里在访问map中的元素时还可以使用->的访问方式(和list类似,这里其实调用了两次->,但是编译器优化成了一个)

在这里插入图片描述

最特别的operator[]

在这里插入图片描述

表面上看起来平平无奇,接下来我们结合使用来感受它的魅力:

在这里插入图片描述

这个就是统计数组中各个元素出现的次数,这种平平无奇的解法相比就没什么需要解释的

除了上面那种老实人解法,如果你灵活使用operator[],可以将上述代码简化
在这里插入图片描述

首先来看一下文档中对该函数的说明在这里插入图片描述

其实operator[]调用的还是insert,所以要把这个理解了,首先要理解insert
在这里插入图片描述

在之前的搜索树和set中因为不允许键值冗余所以插入的返回值就是一个bool值,这里却给了一个迭代器,文档中对返回值这样说:如果不存在这个元素,那么返回的迭代器是新插入的元素的迭代器,second是true,如果该元素已经存在,那么就返回该元素的迭代器,second被设置为false。也就是说,insert还可以充当find来使用;

而operator[]只给了一个key,但是如果map中没有该元素则要求我们插入,那么插入的时候value就会插入类型的匿名对象(如果类型是int,那么匿名对象就是0,指针就是空指针,string就是空串);

对operator[]的整体理解📕:

Value& operator[] (const Key& k)
{
	pair<iterator,bool> ret=insert(make_pair(k,Value() ) );
	return ret.first->second;
    //如果该值存在,返回pair中的first,如果不存在,先插入在返回first;然后再去取pair的second,因为返回的是别名,所以可以修改
}

所以countMap[e]++;大致分为这几步:

1.调用insert,如果该值已经存在,插入失败并返回该节点的迭代器,不存在就插入在返回迭代器

2.根据迭代器获取pair的first,再由first获取到second

3.因为operator[]只给了key,因此value(ret.first->second;)给的是默认值,如果该值不存在,则second是0;最后再对second做++操作


也就是说operator[]兼具三重功能:1.插入 2.查找 3.修改at的功能和[]一样,区别在于用at找不到key将不会发生插入新节点,而是抛出异常。

此后你在面临插入元素时有了更多的写法:

在这里插入图片描述

四.multimap,因为允许键值冗余,所以它没有operator[],它的find返回的是中序遍历第一次遇到的节点


五.两个练习题

前K个高频单词

给定一个单词列表 words 和一个整数 k ,返回前 k 个出现次数最多的单词。返回的答案应该按单词出现频率由高到低排序。如果不同的单词有相同出现频率, 按字典顺序 排序。

输入: words = [“i”, “love”, “leetcode”, “i”, “love”, “coding”], k = 2
输出: [“i”, “love”]
解析: “i” 和 “love” 为出现次数最多的两个单词,均为2次。
注意,按字母顺序 “i” 在 “love” 之前。


解题思路

该题使用map这类KV模型作为存储容器是最好不过了;只是有一点要注意:题目要求如果次数相同就要按字母字典序排列,所以该题不能使用sort(除非定义仿函数重置比较规则0,因为sort底层使用的是快速排序来实现的,而快速排序是一种不稳定的排序;

这里选用使用仿函数重置比较规则以后使用sort的解法

struct Compare
{
    bool operator()(const pair<int,string>& a,const pair<int,string>& b)
    {
        return a.first>b.first || (a.first==b.first&&a.second<b.second);
    }
};
class Solution {
public:
    vector<string> topKFrequent(vector<string>& words, int k) {
        vector<string> word;
        map<string,int> countMap;
        for(const auto& str : words)
        {
            countMap[str]++;
        }
        vector<pair<int,string>> v;
        for(auto& kv : countMap)
        {
            v.push_back(make_pair(kv.second,kv.first));//dataMap的first是string,second是int
        }
        sort(v.begin(),v.end(),Compare());
        for(int i=0;i<k;++i)
        {
            word.push_back(v[i].second);
        }
        return word;
    }
};

两个数组的交集

给定两个数组 nums1nums2 ,返回 它们的交集 。输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序

输入:nums1 = [1,2,2,1], nums2 = [2,2]
输出:[2]

解题思路

输出结果要求每个元素都是唯一的,但是给定的两个数组中有可能出现重复的值,所以可以先使用set做容器存放给定的两个nums数组,不但能排序还可以去重,最后再遍历数组找相同。

class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
        set<int> s1(nums1.begin(),nums1.end());//nums1排序+去重    
        set<int> s2(nums2.begin(),nums2.end());//nums2排序+去重
        vector<int> ret;
        for(auto& e : s1)
        {
            if(s2.find(e)!=s2.end())//在s2中查找s1的元素
            {
                ret.push_back(e);
            }
        }
        return ret;
    }
};

当然你也可以先将数组去重+排序,然后使用双指针求解

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

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

相关文章

ChatGPT办公自动化实战

ChatGPT从入门到精通&#xff0c;一站式掌握办公自动化/爬虫/数据分析和可视化图表制作 全面AI时代就在转角 道路已经铺好了 “局外人”or“先行者” 就在此刻 等你决定 让ChatGPT帮你高效实现职场办公&#xff01;行动起来吧 1、ChatGPT从入门到精通&#xff0c;一站式掌握办…

对象的销毁

析构函数 C 中的类可以定义一个特殊的清理函数 这个特殊的清理函数叫做析构函数析构函数的功能与构造函数相反 定义&#xff1a;~ClassName() 析构函数没有参数也没有返回值类型声明析构函数在对象销毁时自动被调用 析构函数使用初探 #include <stdio.h>class Test …

Threadlocal 必会的9个知识点

1.什么是ThreadLocal&#xff1f;它在多线程环境下有什么用处&#xff1f; ThreadLocal是在多线程环境下提供的一种简单的机制&#xff0c;使得每个线程都能拥有一个独立的变量副本。它避免了线程安全问题&#xff0c;并提高了程序的并发性能。 2.ThreadLocal是如何工作的&am…

规则引擎--规则逻辑形如“1 (2 | 3)“的抽象设计

目录 规则下逻辑表达和条件的抽象表达逻辑的编码和抽象 规则规则下的条件操作符抽象定义规则类规则执行表达式遍历进行操作符计算添加规则下一个具体条件的执行 规则执行完成后得到最后的结果 规则下逻辑表达和条件的抽象 对于任何一个规则&#xff0c;当然包括多个条件&#…

市面上最强PDF:GcPDF 6.1.4 Grapecity -Crack

适用于 .NET 6 的功能丰富的 PDF API 库 完全控制 PDF - 快速生成文档、提高内存效率且无依赖性。 在代码中生成、加载、编辑和保存 PDF 文档 支持多种语言的全文、段落格式和字体 使用新的编辑工具编辑 PDF 中的内容 支持数百种PDF功能 Windows、macOS 和 Linux 完全支持所有…

PhotoShop Beta(爱国版)安装教程-内置AI绘画功能

PS beta版安装教程 Window和Mac版都有&#xff0c;里面内置AI绘画功能 ps Beta版真的太爽了&#xff0c;今天来和大家分享下安装教程。 很多人拿这资料卖5块 9.9 19.9&#xff0c;球友们直接用&#xff0c;建议赶紧装&#xff0c;以免PS更新后&#xff0c;很多pojie程序没法用了…

ChatGPT数据分析与可视化实战

ChatGPT从入门到精通&#xff0c;一站式掌握办公自动化/爬虫/数据分析和可视化图表制作 全面AI时代就在转角 道路已经铺好了 “局外人”or“先行者” 就在此刻 等你决定 让ChatGPT帮你高效实现职场办公&#xff01;行动起来吧1、ChatGPT从入门到精通&#xff0c;一站式掌握办…

docker安装drone

目录 Drone简介docker安装drone创建Drone-server容器创建Drone-runner-docker容器 访问drone-server面板操作 Drone简介 Drone是基于GO语言开发的持续集成&#xff08;Continuous integration&#xff0c;CI&#xff09;引擎&#xff0c;它可以借助Docker容器技术&#xff0c;…

Autosar RTE C/S接口实现及synchronous与asynchronous的区别

文章目录 前言Server接口设计server接口Simulink实现server函数mapping Function生成的代码 Client接口设计Client接口Simulink实现ClientFunction Caller Mapping生成的代码Rte_CallRte_Result 总结 前言 在之前的一篇文章中&#xff0c;介绍了RTE中的S/R接口&#xff0c;也是…

(一)WPF - WPF

一、Window 图形演化 创建用户界面&#xff1a; User32&#xff1a; 该部分为许多元素&#xff08;如窗口、按钮和文本框等&#xff09;提供了熟悉的 Windows 外观。GDI/GDI&#xff1a; 该部分为渲染简单形状、文本以及图像提供了绘图支持&#xff0c;但增加了复杂程度&…

Nginx使用

说明&#xff1a;Nginx是静态资源服务器&#xff0c;可以部署静态资源&#xff0c;并对请求进行策略分发。 下载 第一步&#xff1a;可在官网&#xff08;http://nginx.org/en/download.html&#xff09;下载&#xff0c;建议安装稳定版本&#xff08;Stable version&#xf…

【力扣刷题 | 第十三天】

前言&#xff1a; 今天随机进行练习&#xff0c;题型上不会有什么限制&#xff0c;主要还是练习STL算法。 88. 合并两个有序数组 - 力扣&#xff08;LeetCode&#xff09; 给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2&#xff0c;另有两个整数 m 和 n &#xff0c;分…

[RocketMQ] Broker与NameServer的心跳服务源码 (四)

文章目录 1.Broker发送心跳注册请求源码1.1 发送心跳包入口1.2 registerBrokerAll注册broker信息 2.NameServer处理心跳注册请求2.1 处理心跳包入口2.2 NameServer注册broker信息2.2.1 RouteInfoManager介绍2.2.2 registerBroker注册broker 3.NameServer的心跳检测服务3.1 scan…

GIS坐标系统

最新在看GIS的理论知识&#xff0c;坐标系统这块比较抽象&#xff0c;B站上搜到到一个博主的视频&#xff0c;对这块讲解的比较通俗易懂&#xff0c;这里记录一下&#xff1a; 地理坐标系统 地理坐标系统是地球表面空间要素的定位参照系统。地理坐标系统是由经度和维度定义的。…

记录Unity Endless Runner要点

1. Array.IndexOf()查找数组中指定项的索引&#xff0c;如果没找到&#xff0c;就返回-1 2. 如果粒子不是循环播放的&#xff0c;则在粒子播放完毕之后销毁它 if (!m_ParticleSpawned.main.loop)Destroy(m_ParticleSpawned.gameObject, m_ParticleSpawned.main.duration); 3. 检…

普通单目相机标定

前言 这里我们还是以普通相机为例(非鱼眼相机)来进行后续的相关标定操作,再回顾下相机的成像模型如下所示。 已知相机内参(fx,fy,u0,v0),畸变系数[k1,k2,k3,p1,p2],相机外参[R|T]。世界坐标系中点Pw(Xw,Yw,Zw),投影至像素坐标系点p(u,v)的计算过程如下。 1)由世…

操作系统———文件管理

目录 一、初识文件管理1.文件属性2.文件内部数据组织3.文件之间组织4.操作系统向上提供的功能5.文件如何存放在外存6.其他需要由操作系统实现的文件管理功能7.总结 二、文件的逻辑结构1.无结构文件与有结构文件2.有结构文件的逻辑结构2.1顺序文件2.2索引文件2.3索引顺序文件 3.…

ChatGPT 指令知识要点

ChatGPT从入门到精通&#xff0c;一站式掌握办公自动化/爬虫/数据分析和可视化图表制作 全面AI时代就在转角 道路已经铺好了 “局外人”or“先行者” 就在此刻 等你决定1、ChatGPT从入门到精通&#xff0c;一站式掌握办公自动化/爬虫/数据分析和可视( 点击观看完整版本 )https…

Linux下MySQL的安装

文章目录 下载1.选择合适的yum源2.将yum源上传到Linux服务器中 安装1.安装yum源2.使用yum源一键安装MySQL3.安装时常见的问题4.检查安装 启动MySQL登录MySQL方案一方案二 下载 1.选择合适的yum源 在Linux学习阶段我们已经得知&#xff0c;在Linux环境下要安装应用程序必须要通…