【高阶数据结构】map和set的介绍和使用 {关联式容器;键值对;map和set;multimap和multiset;OJ练习}

news2025/1/23 10:35:30

map和set的介绍和使用

一、关联式容器

关联式容器和序列式容器是C++ STL中的两种不同类型的容器。

  • 关联式容器是基于键值对的容器,其中每个元素都有一个唯一的键值,可以通过键值来访问元素。关联式容器包括set、multiset、map和multimap。

  • 序列式容器是基于元素序列的容器,其中元素按照一定的顺序排列,可以通过元素的位置来访问元素。序列式容器包括vector、deque、list和array。

  • 关联式容器和序列式容器的主要区别在于它们的存储方式和访问方式。关联式容器使用二叉搜索树等数据结构来存储元素,可以快速地查找元素,但是插入和删除元素的效率较低。序列式容器使用数组或链表等数据结构来存储元素,可以快速地插入和删除元素,但是查找元素的效率较低。

在选择使用关联式容器还是序列式容器时,需要根据具体的需求来进行选择。如果需要按照键值来查找并访问元素,可以选择关联式容器;如果需要按照元素的位置来访问元素,可以选择序列式容器。

  • 根据应用场景的不同,STL总共实现了两种不同结构的关联式容器:树型结构与哈希结构。
  • 树型结构的关联式容器主要有四种:set、multiset、map、multimap。
  • 这四种容器的共同点是:使用平衡搜索树(即红黑树)作为其底层结果,容器中的元素是一个有序的序列。

二、键值对

在这里插入图片描述

用来表示具有一一对应关系的一种结构,该结构中一般只包含两个成员变量key和value,key代表键值,value表示与key对应的信息。比如:现在要建立一个英汉互译的字典,那该字典中必然有英文单词与其对应的中文含义,而且,英文单词与其中文含义是一一对应的关系,即通过该英文单词,在词典中就可以找到与其对应的中文含义。

在C++中的键值对结构是pair,它是一个类模版。下面是SGI-STL中关于键值对的定义:

template <class T1, class T2>
struct pair
{
	typedef T1 first_type;
	typedef T2 second_type;
	T1 first; //代表键值key
	T2 second; //表示与key对应的信息value
	pair(): first(T1()), second(T2())
	{}
	pair(const T1& a, const T2& b): first(a), second(b)
	{}
};

注意:pair类在头文件<utility>中声明,<utility>又被头文件<map>包含。


三、set的介绍和使用

3.1 set的介绍

  1. set是按照一定次序存储元素的容器,底层实际就是平衡二叉搜索树(红黑树)的K模型。

  2. set中的元素不能在容器中修改(元素总是const),但是可以从容器中插入或删除它们。

  3. 在内部,set中的元素总是按照其内部比较对象(仿函数)所指示的特定严格弱排序准则进行排序。

注意:

  1. set中只放键值key,插入元素时,只需要插入key即可,不需要构造键值对。
  2. set中的元素不可以重复,因此可以使用set进行去重
  3. 使用set的迭代器遍历set中的元素,可以得到有序序列
  4. set中查找某个元素,时间复杂度为:log_2 n
  5. set中的元素不允许修改,因为修改set中的元素会破坏二叉搜索树结构。

set的模板参数列表

在这里插入图片描述

  • T: set中存放元素的类型,也就是键值key的类型。
  • Compare:比较对象(仿函数),set中元素默认按照小于来比较。
  • Alloc:set中元素空间的管理方式,默认使用STL提供的空间配置器管理。

3.2 set的使用

3.2.1 Constructor

在这里插入图片描述


3.2.2 Iterator

在这里插入图片描述

注意:set的迭代器是按照搜索二叉树的中序遍历顺序遍历set中的元素,所以当比较对象默认为Less时:set正向迭代器的遍历结果是升序序列;逆向迭代器的遍历结果是降序序列;


3.2.3 Modifiers

insert && erase

在这里插入图片描述

在这里插入图片描述


swap

在这里插入图片描述

调用set::swap完成交换后,容器中的元素是调用前 x 中的元素。所有的迭代器,引用,指针对交换后的对象仍然有效。

底层原理:set::swap是浅交换,只是将两个set中的root指针做了交换。


3.2.4 Operations

find

在这里插入图片描述

返回值:找到了返回元素的迭代器;找不到返回set::end;


count

在这里插入图片描述


lower_bound && upper_bound

在这里插入图片描述

返回值:返回容器中>=val的第一个元素的迭代器

在这里插入图片描述

返回值:返回容器中>val的第一个元素的迭代器


equal_range
在这里插入图片描述

返回值:用键值对pair返回容器中等于val的元素的迭代器范围,其中first<=valsecond>val


3.3 multiset的介绍和使用

multiset:允许存在键值相等的元素(允许键值冗余)

在这里插入图片描述

multiset和set拥有相同的成员函数,但用法略有不同:

  1. multiset::find:如果有多个键值相等的元素,返回中序遍历中的第一个。
  2. multiset::erase(val):如果有多个键值相等的元素,将所有值为val的元素全部删除。
  3. multiset::count(val):统计容器中值为val的元素个数。

测试代码:

void Test1(){
  multiset<int> ms = {1,4,2,6,3,7,4,2,4,1}; //c++11语法:初始化列表构造
  multiset<int>::iterator it = ms.begin();
  while(it != ms.end())
  {    
    cout << *it << " ";    
    ++it;    
  }    
  cout << endl;    
    
  cout << "test_erase:";    
  ms.erase(1);    
  it = ms.begin(); 
  //......
  //遍历输出ms
  cout << endl;    
    
  cout << "test_find:";
  auto pos = ms.find(4);
  //......
  //从pos开始遍历输出ms
  cout << endl;
    
  cout << "test_count:";
  cout << ms.count(4) << endl;  
}

运行结果:

在这里插入图片描述


四、map的介绍和使用

4.1 map的介绍

  1. map是按照特定次序存储<key,value>键值对的容器,底层实际就是平衡二叉搜索树(红黑树)的KV模型。

  2. 在map中,键值key通常用于排序和惟一标识元素,而值value中存储与此键值key关联的内容。键值key和值value的类型可能不同,并且在map的内部,key与value通过成员类型value_type绑定在一起,value_type是键值对pair类型。在这里插入图片描述

  3. 在内部,map中的元素总是按照键值key进行比较排序的。

map的模版参数列表

在这里插入图片描述

  • key: 键值对中key的类型
  • T: 键值对中value的类型
  • Compare: 比较器的类型,map中的元素是按照key来比较的,缺省情况下按照小于来比较,一般情况下(内置类型元素)该参数不需要传递,如果无法比较时(自定义类型),需要用户自己显式传递比较规则(一般情况下按照函数指针或者仿函数来传递)
  • Alloc:通过空间配置器来申请底层空间,不需要用户传递,除非用户不想使用标准库提供的空间配置器

注意:在使用map时,需要包含头文件<map>。


4.2 map的使用

map和set的使用大体相同,不做过多介绍。举两个例子具体来看:在上一章节《二叉搜索树》的KV模型应用部分,有两个应用实例:用二叉搜索树的KV模型实现字典和单词计数器。现在我们用map来实现:

4.2.1 例一:Dictionary

void Dictionary(){
  map<string, string> dict;
  //map存储的元素是键值对pair,有以下几种pair的构造插入方法:
  //1.匿名对象构造法
  dict.insert(pair<string, string>("string", "字符串"));
  //2.typedef类型重命名,缩短类型名
  typedef pair<string, string> DictKV;
  //3.通过make_pair函数模版构造键值对(推荐)
  dict.insert(DictKV("peach", "桃子"));    
  dict.insert(make_pair("dictionary", "字典"));    
  dict.insert(make_pair("temporary", "短暂的"));
  dict.insert(make_pair("declaration", "声明"));    
    
  //map的迭代器遍历
  //map的迭代器底层是指向pair键值对结构的指针
  //map<string, string>::iterator it = dict.begin();                   
  auto it = dict.begin(); //map迭代器的类型过长,通过auto自动识别类型    
  while(it != dict.end())                        
  {    
    //cout << (*it).first << ":" << (*it).second << endl; //解引用后通过.访问pair的成员    
    cout << it->first << ":" << it->second << endl;//直接通过->访问pair的成员(推荐)  
    ++it;    
  } 
   
  //范围for:
  //for(pair<const string, string> &e : dict) //map存储的元素是键值对pair,注意key是const类型
  for(auto &e : dict) //临时变量e应该取引用,避免发生拷贝构造,浪费资源   
  {    
    cout << e.first << ":" << e.second << endl; 
  }
    
  string input;
  while(cin >> input)                     
  {
    auto ret = dict.find(input); //传入key值,返回对应元素(pair)的迭代器
    if(ret != dict.end()) //找不到返回map::end
    {
      cout << ret->first << ":" << ret->second << endl;
    }
    else{
      cout << "There is no such word!" << endl;
    }
  }
}

make_pair

在这里插入图片描述

在这里插入图片描述

  1. make_pair是一个函数模版,作用是利用传入的两个参数构造键值对pair并返回(传值返回)。

  2. 利用了函数模版的隐式实例化:通过传入的参数类型自动推导pair的模版参数。

  3. make_pair在头文件<utility>中声明,<utility>又被头文件<map>包含。


4.2.2 例二:WordCounter

void WordCounter(){
  map<string, int> counter;
  string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜","苹果", "香蕉", "苹果", "香蕉"};
  //方法一:上一章讲过的方法
  //for(string &e : arr)
  //{
  //  auto ret = counter.find(e);
  //  if(ret != counter.end())
  //  {
  //    ++ret->second; 
  //  } 
  //  else{
  //    counter.insert(make_pair(e,1));                                                                                          
  //  }
  //}
    
  //方法二:operator[](推荐)
  for(string &e : arr)
  {
    //如果e不在counter中,先插入pair(e,int()),再对返回value(次数)++;
    //如果e在counter中,返回value(次数)的引用,次数++;
    ++counter[e];
  }
  for(auto &e : counter)
  {
    cout << e.first << ":" << e.second << endl;
  }
}

operator[]

在这里插入图片描述

  1. map支持operator[],但与vector等连续存储容器的位置下标访问不同,map支持关键字下标访问,[]中填入key值:

  2. 如果map中有这个key,返回对应value的引用。(查找+修改value)

  3. 如果map中没有这个key,会插入一个pair(key,value()),并返回新创建的value的引用。(插入+修改value)

  4. 所谓pair(key,value())使用给定的key和value的默认构造创建的pair键值对。

  5. map::at是一个和operator[]功能相似的成员函数。当key存在时和[]有相同的行为;但当key不存在时,at不会创建新元素而是直接抛异常。

  6. operator[]的底层实现相当于:

    mapped_type& operator[] (const key_type& k)
    {
        return (*((this->insert(make_pair(k,mapped_type()))).first)).second;
    }
    //下面代码的可读性更高,更简洁。
    mapped_type& operator[] (const key_type& k)
    {
        pair<iterator, bool> ret = insert(make_pair(k,mapped_type()));
        return ret.first->second;
    }
    

insert

在这里插入图片描述

  1. 如果key已经在map中,插入失败,返回pair(key_iterator, false);
  2. 如果key不在map中,插入成果,返回pair(newkey_iterator, true);

4.3 multimap的介绍和使用

  1. multimap和map的唯一不同就是:map中的key是唯一的,而multimap中允许存在键值相等的元素(允许键值冗余)
  2. 没有重载operator[]:由于键值冗余,无法确定唯一的value。
  3. multimap中的接口可以参考map,功能都是类似的。

五、OJ练习

692. 前K个高频单词

349. 两个数组的交集

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

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

相关文章

从零做软件开发项目系列之七——用户培训

开展用户培训工作是确保软件项目成功的重要一环。以下是一个详细的步骤指南&#xff0c;用于规划和执行用户培训。 1. 确定培训目标和用户。 首先需要识别用户角色。确定项目中不同用户角色和职能&#xff0c;例如管理员、操作员、管理人员等。不同角色可能需要不同的培训内容…

14.工作式指针和数字时钟

源码 源码 <!doctype html> <html><head><meta charset="utf-8"><title>Clock</title><link rel="stylesheet" href="style.css"></head><body><div class="container">…

epoll 基于多线程的边沿非阻塞处理

往期文章推荐&#xff1a; epoll() 多路复用 和 两种工作模式_呵呵哒(&#xffe3;▽&#xffe3;)"的博客-CSDN博客https://blog.csdn.net/weixin_41987016/article/details/132523789?spm1001.2014.3001.5501 epoll_server.c #include <stdio.h> #include &l…

python venv 打包,更换路径后,仍然读取到旧路径 ,最好别换路径,采用docker封装起来

机械盘路径 /home/yeqiang/code/xxx 移动到 /opt/xxx 编辑/opt/xxx/venv/bin/activate VIRTUAL_ENV"/home/yeqiang/code/xxx/venv" 改为 VIRTUAL_ENV"/opt/xxx/venv" 下面还有这么多&#xff0c;参考&#xff1a; (venv) yeqiangyeqiang-MS-7B23:/…

Kubernetes入门 十一、网络之Service

目录 概述Service 原理Service 四种类型创建 Service代理 k8s 外部服务反向代理外部域名 概述 在 Kubernetes 中&#xff0c;Pod 是应用程序的载体&#xff0c;我们可以通过 Pod 的 IP 来访问应用程序&#xff0c;但是 Pod 的 IP 地址不是固定的&#xff0c;这就意味着不方便直…

MyBatisPlus实现多租户功能

前言&#xff1a;多租户是一种软件架构技术&#xff0c;在多用户的环境下&#xff0c;共有同一套系统&#xff0c;并且要注意数据之间的隔离性。 一、SaaS多租户简介 1.1、SaaS多租户 SaaS&#xff0c;是Software-as-a-Service的缩写名称&#xff0c;意思为软件即服务&#x…

【电源专题】读一读单节锂电池保护IC规格书

在文章【电源专题】单节锂离子电池的保护的基本原理 中我们了解了电池包的过充、过放、过流、短路等保护功能。那么这些功能都会在电池保护IC规格书中体现吗?体现在哪些地方?哪些参数是我们应关注的呢? 对于手册中的电压检测,如放电过流、充电过流和负载短路等检测电压都代…

开源软件的国际化和本地化

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…

centos7删除乱码文件

centos7删除乱码文件1. 小白教程&#xff0c;一看就会&#xff0c;一做就成。 1.解释 当文件名为乱码的时候&#xff0c;无法通过键盘输入文件名&#xff0c;所以在终端下就不能直接利用rm&#xff0c;mv等命令管理文件了。 但是每个文件都有一个i节点号&#xff0c;可以通过…

《Flink学习笔记》——第三章 Flink的部署模式

不同的应用场景&#xff0c;有时候对集群资源的分配和占用有不同的需求。所以Flink为各种场景提供了不同的部署模式。 3.1 部署模式&#xff08;作业角度/通用分类&#xff09; 根据集群的生命周期、资源的分配方式、main方法到底在哪里执行——客户端还是Client还是JobManage…

AIGC - 生成模型

AIGC - 生成模型 0. 前言1. 生成模型2. 生成模型与判别模型的区别2.1 模型对比2.2 条件生成模型2.3 生成模型的发展2.4 生成模型与人工智能 3. 生成模型示例3.1 简单示例3.2 生成模型框架 4. 表示学习5. 生成模型与概率论6. 生成模型分类小结 0. 前言 生成式人工智能 (Generat…

【最强最全】视频号下载助手(支持视频号视频, 直播,回放下载)

视频号下载助手支持视频号视频, 直播,回放的下载&#xff0c;本工具基于秦天sunny中间件编写&#xff0c;无需再使用其它抓包软件&#xff0c;无需再使用其它下载软件。 当然&#xff0c;你也可以右键复制抓取后的视频源再用其它下载软件下载。 使用说明 解压文件&#xff0c;…

CSS中如何实现弹性盒子布局(Flexbox)的换行和排序功能?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 换行&#xff08;Flexbox Wrapping&#xff09;⭐ 示例&#xff1a;实现换行⭐ 排序&#xff08;Flexbox Ordering&#xff09;⭐ 示例&#xff1a;实现排序⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 记得…

基于java swing和mysql实现的仓库商品管理系统(源码+数据库+运行指导视频)

一、项目简介 本项目是一套基于java swing和mysql实现的仓库商品管理系统&#xff0c;主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的Java学习者。 包含&#xff1a;项目源码、项目文档、数据库脚本等&#xff0c;该项目附带全部源码可作为毕设使用。 项目都经…

流媒体弱网优化之路(BBR应用)——GCC与BBR的算法思想分析

流媒体弱网优化之路(WebRTC)——GCC与BBR的算法思想分析 —— 我正在的github给大家开发一个用于做实验的项目 —— github.com/qw225967/Bifrost目标&#xff1a;可以让大家熟悉各类Qos能力、带宽估计能力&#xff0c;提供每个环节关键参数调节接口并实现一个json全配置&…

【洛谷算法题】P1001-A+B Problem【入门1顺序结构】

&#x1f468;‍&#x1f4bb;博客主页&#xff1a;花无缺 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! 本文由 花无缺 原创 收录于专栏 【洛谷算法题】 文章目录 【洛谷算法题】P1001-AB Problem【入门1顺序结构】&#x1f30f;题目背景&#x1f30f;题目描述…

【Linux操作系统】Linux系统编程中条件变量实现生产者消费者模型

在Linux系统编程中&#xff0c;条件变量是一种用于线程间同步的机制&#xff0c;常用于实现生产者消费者模型。生产者消费者模型是一种常见的并发编程模型&#xff0c;用于解决多线程环境下的数据共享和同步问题。在该模型中&#xff0c;生产者负责生产数据&#xff0c;消费者负…

53 个 CSS 特效 3(完)

53 个 CSS 特效 3&#xff08;完&#xff09; 前两篇地址&#xff1a; 53 个 CSS 特效 153 个 CSS 特效 2 这里是第 33 到 53 个&#xff0c;很多内容都挺重复的&#xff0c;所以这里解释没之前的细&#xff0c;如果漏了一些之前的笔记会补一下&#xff0c;写过的就会跳过。…

【算法训练-模拟】模拟设计LRU缓存结构

废话不多说&#xff0c;喊一句号子鼓励自己&#xff1a;程序员永不失业&#xff0c;程序员走向架构&#xff01;本篇Blog的主题是LRU缓存结构设计&#xff0c;这类题目出现频率还是很高的&#xff0c;几乎所有大厂都常考。 当然面对这道题&#xff0c;首先要讲清楚LRU是干什么…