【C++】map 和 set

news2024/11/24 18:53:24

目录

一 基础概念

1 关联式容器

2 键值对

3 树形结构的关联式容器

二 map

1 概念

2 基础操作

3 使用实列

1 实例一

2 实例二

3 实例三 

4 实例四 

4 multimap 

1 实例一

三 set

1 概念

2 基础操作

3 使用实例

1 实例一

2 实例二

3 实例三

4 multiset

1 实例一 


一 基础概念

1 关联式容器

STL中的部分容器,比如:vector、list、deque等,这些容器统称为序列式容器,因为其底层为线性序列的数据结构,里面存储的是元素本身。那什么是关联式容器?它与序列式容器有什么区别?

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

2 键值对

用来表示具有一一对应关系的一种结构,该结构中一般只包含两个成员变量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)
       {}
};

3 树形结构的关联式容器

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

二 map

1 概念

1. map是关联容器,它按照特定的次序(按照key来比较)存储由键值key和值value组合而成的元素。

2. 在map中,键值key通常用于排序和惟一地标识元素,而值value中存储与此键值key关联的内容。键值key和值value的类型可能不同,并且在map的内部,key与value通过成员类型value_type绑定在一起,为其取别名称为pair : typedef pair<const key, T> value_type;

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

4. map中通过键值访问单个元素的速度通常比unordered_map容器慢,但map允许根据顺序对元素进行直接迭代(即对map中的元素进行迭代时,可以得到一个有序的序列)。

5. map支持下标访问符,即在[]中放入key,就可以找到与key对应的value。

6. map通常被实现为二叉搜索树(更准确的说:平衡二叉搜索树(红黑树))。

map的查询效率是O(log_2N)是正确的,但map的底层结构不是二叉搜索树,而是红黑树

2 基础操作

 

注意:在元素访问时,有一个与operator[]类似的操作at()(该函数不常用)函数,都是通过 key找到与key对应的value然后返回其引用,不同的是:当key不存在时,operator[]用默认 value与key构造键值对然后插入,返回该默认value,at()函数直接抛异常。

3 使用实列

1 实例一

void test_map1()
{
       map<string, string> dict;
       dict.insert(pair<string, string>("sort", "排序"));

       //pair<string, string> kv("string", "字符串");
       pair<string, string> kv = { "string", "字符串" };
       dict.insert(kv);

       // C++11 多参数隐式类型转换(构造函数)
       dict.insert({ "apple", "苹果" });

       // C++98
       dict.insert(make_pair("sort", "排序"));

       //map<string, string>::iterator it = dict.begin();
       auto it = dict.begin();
       while (it != dict.end())
       {
              //cout << (*it).first << (*it).second << endl;
              cout << it->first << it->second << endl;
              ++it;
       }
       cout << endl;

       for (auto& kv : dict)
       {
              cout << kv.first << ":" << kv.second << endl;
       }
       cout << endl;
}


int main()
{
       /*test_set1();*/
       /*test_set2();*/
       /*test_set3();*/
       test_map1();
       return 0;
}

2 实例二

void test_map2()
{
       // key相同,value不同,不会插入也不会更新
       map<string, string> dict;
       dict.insert(make_pair("sort", "排序"));
       dict.insert(make_pair("string", "字符串"));

       dict["left"]; // 插入
       cout << dict["sort"] << endl; // 查找

       dict["sort"] = "xxx"; // 修改
       dict["right"] = "右边"; // 插入+修改

       for (auto& kv : dict)
       {
              cout << kv.first << ":" << kv.second << endl;
       }
       cout << endl;
}

int main()
{
       /*test_set1();*/
       /*test_set2();*/
       /*test_set3();*/
       /*test_map1();*/
       test_map2();
       return 0;
}

3 实例三 

void test_map3()
{
       string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜",
"苹果", "香蕉", "苹果", "西瓜", "香蕉", "草莓" };
       map<string, int> countMap;
       //统计个数

       //方法1
       //for (auto& e : arr)
       //{
       //     map<string, int>::iterator it = countMap.find(e);
       //     if (it != countMap.end())
       //     {
       //            it->second++;
       //     }
       //     else
       //     {
       //            countMap.insert(make_pair(e, 1));
       //     }
       //}


       //方法2
       //for (auto& e : arr)
       //{
       //     pair<map<string, int>::iterator, bool> ret;
       //     ret = countMap.insert(make_pair(e, 1));

       //     // 已经存在了
       //     if (ret.second == false)
       //     {
       //            ret.first->second++;
       //     }
       //}

       //方法3
       for (auto& e : arr)
       {
              countMap[e]++;
       }

       for (auto& kv : countMap)
       {
              cout << kv.first << ":" << kv.second << endl;
       }
       cout << endl;
}
int main()
{
       /*test_set1();*/
       /*test_set2();*/
       /*test_set3();*/
       /*test_map1();*/
       test_map3();
       return 0;
}

而[ ] 底层大概这样

V& operator[](const K& key)
{
       pair<iterator, bool> ret = insert(make_pair(key, V()));
       return ret.first->second;
}

4 实例四 

降序

int main()
{
    map<int, int, greater<int>> m;
    m.insert(make_pair(1, 2));
    m.insert(make_pair(2, 2));
    m.insert(make_pair(3, 2));
    m.insert(make_pair(4, 2));
    m.insert(make_pair(5, 2));
    for (auto& e : m)
    {
        cout << e.first << ' ' ;
    }
    return 0;
}

4 multimap 

Multimaps是关联式容器,它按照特定的顺序,存储由key和value映射成的键值对<key,value>,其中多个键值对之间的key是可以重复的。

注意:multimap和map的唯一不同就是:map中的key是唯一的,而multimap中key是可以重复的。

multimap中的接口可以参考map,功能都是类似的。

注意:

1. multimap中的key是可以重复的。

2. multimap中的元素默认将key按照小于来比较

3. multimap中没有重载operator[]操作。

4. 使用时与map包含的头文件相同

1 实例一

void test_map4()
{
       multimap<string, string> dict;
       dict.insert(make_pair("sort", "排序"));
       dict.insert(make_pair("string", "字符串"));
       dict.insert(make_pair("sort", "xxx"));
       dict.insert(make_pair("sort", "排序"));

       for (auto& kv : dict)
       {
              cout << kv.first << ":" << kv.second << endl;
       }
       cout << endl;
}
int main()
{
       /*test_set1();*/
       /*test_set2();*/
       /*test_set3();*/
       /*test_map1();*/
       /*test_map3();*/
       test_map4();
       return 0;
}

三 set

1 概念

1. set是按照一定次序存储元素的容器

2. 在set中,元素的value也标识它(value就是key,类型为T),并且每个value必须是唯一的。set中的元素不能在容器中修改(元素总是const),但是可以从容器中插入或删除它们。

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

4. set容器通过key访问单个元素的速度通常比unordered_set容器慢,但它们允许根据顺序对子集进行直接迭代。

5. set在底层是用二叉搜索树(红黑树)实现的

注意:

1. 与map / multimap不同,map / multimap中存储的是真正的键值对<key, value>,set中只放value,但在底层实际存放的是由<value, value>构成的键值对。

2. set中插入元素时,只需要插入value即可,不需要构造键值对。

3. set中的元素不可以重复(因此可以使用set进行去重)。

4. 使用set的迭代器遍历set中的元素,可以得到有序序列

5. set中的元素默认按照小于来比较

6. set中查找某个元素,时间复杂度为:$log_2 n$

7. set中的元素不允许修改

元素的哈希性要求: 在集合中,元素的存储和查找依赖于哈希值。为了保持高效的查找和插入操作,集合要求所有元素必须是可哈希的(即哈希值在其生命周期中保持不变)。如果允许修改集合中的元素,元素的哈希值可能会发生变化,这将破坏集合的结构,导致查找和操作失效。因此,集合要求其元素是不可变的对象

8. set中的底层使用二叉搜索树(红黑树)来实现。

2 基础操作

 

3 使用实例

1 实例一

void test_set1()
{
       // 排序+去重
       set<int> s;
       s.insert(5);
       s.insert(1);
       s.insert(6);
       s.insert(3);
       s.insert(4);
       s.insert(5);
       s.insert(1);

       set<int>::iterator it = s.begin();
       //找不到返回s.end()
       while (it != s.end())
       {
              cout << *it << " ";
              ++it;
       }
       cout << endl;

       set<int>::iterator pos = s.find(5);
       //找不到返回s.end()
       if (pos != s.end())
       {
              cout << "找到了" << endl;
              s.erase(pos);
       }

       for (auto e : s)
       {
              cout << e << " ";
       }
       cout << endl;

       // 在就删除,不在就不做任何处理
       s.erase(3);
       s.erase(30);
       for (auto e : s)
       {
              cout << e << " ";
       }
       cout << endl;

       // 这个值在,找到有效位置,再进行删除
       //pos = s.find(5);
       //s.erase(pos);

       //count--> 返回该元素个数 有返回1 没有返回0
       if (s.count(5))
       {
              cout << "在" << endl;
       }
       else
       {
              cout << "不在" << endl;
       }
}


int main()
{
       test_set1();
       return 0;
}

2 实例二

void test_set2()
{
       // 排序+去重
       set<int> s;
       s.insert(5);
       s.insert(1);
       s.insert(6);
       s.insert(3);
       s.insert(4);
       for (auto e : s)
       {
              cout << e << " ";
       }
       cout << endl;


       auto start = s.lower_bound(3);  // 返回 >=val 的值
       cout << *start << endl;

       auto finish = s.upper_bound(5);  // 返回 >val 的值
       cout << *finish << endl;

       //找 [3, 5]区间的数
       while (start != finish)
       {
              cout << *start << " ";
              ++start;
       }
       cout << endl;

       start = s.lower_bound(3);
       //删除[3, 5]区间的数字
       s.erase(start, finish);
       for (auto e : s)
       {
              cout << e << " ";
       }
       cout << endl;
}

int main()
{
       /*test_set1();*/
       test_set2();
       return 0;
}

3 实例三

#include<set>
int main()
{
    set<int, greater<int>> s = { 1, 2, 3, 4, 5 };
    for (auto e : s)
    {
        cout << e << ' ';
    }
    return 0;
}

4 multiset

multiset是按照特定顺序存储元素的容器,其中元素是可以重复的。

multiset底层结构为二叉搜索树(红黑树)。

操作可以参考set

1 实例一 

void test_set3()
{
       // 排序
       multiset<int> s;
       s.insert(5);
       s.insert(1);
       s.insert(6);
       s.insert(3);
       s.insert(4);
       s.insert(5);
       s.insert(1);
       s.insert(1);
       s.insert(5);
       s.insert(1);
       s.insert(1);
       s.insert(2);
       s.insert(7);
       s.insert(10);


       multiset<int>::iterator it = s.begin();
       while (it != s.end())
       {
              cout << *it << " ";
              ++it;
       }
       cout << endl;

       //count 返回该value的个数
       cout << s.count(5) << endl;
       cout << s.count(1) << endl;

       it = s.find(5);
       while (it != s.end() && *it == 5)
       {
              cout << *it << " ";
              ++it;
       }
       cout << endl;
}

int main()
{
       /*test_set1();*/
       /*test_set2();*/
       test_set3();
       return 0;
}

本节是对后面的hash 的过度, 内容还是很简单的, 后续还会模拟实现它们, 继续加油! 

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

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

相关文章

SpringBoot使用esayExcel根据模板导出excel

1、依赖 <dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.1.3</version></dependency> 2、模板 3、实体类 package com.skybird.iot.addons.productionManagement.qualityTesting…

流程图 LogicFlow

流程图 LogicFlow 官方文档&#xff1a;https://site.logic-flow.cn/tutorial/get-started <script setup> import { onMounted, ref } from vue import { forEach, map, has } from lodash-es import LogicFlow, { ElementState, LogicFlowUtil } from logicflow/core …

字符编码发展史6 — BOM字节序标记

上一篇《字符编码发展史5 — UTF-16和UTF-32》我们讲解了UTF-16和UTF-32编码。本篇我们将继续讲解字符编码中的字节序标记(BOM)。 2.3. 第三个阶段 国际化 2.3.2. Unicode的编码方式 2.3.2.5. BOM 1. 什么是BOM&#xff1f; BOM是Byte Order Mark的缩写&#xff0c;翻译成…

研究生异地报名,需要社保缴费记录,没有社保记录怎么办。

1、户籍在安徽省&#xff0c;在北京工作&#xff0c;想报北京科技大学&#xff1b; 招生简章中没有提社保记录&#xff0c;但是在报名的时候&#xff0c;又出来要求&#xff1a;北京连续6个月的社保记录。这里是指在北京市考试的要求。没有连续社保缴费记录&#xff0c;肯定不能…

Python 与 Pycharm 的简易安装教程,包含Pycharm的修改

一. 官方网站 Python网址&#xff1a;python唯一的官方网址。 Pycharm网址&#xff1a;Pycharm的官方网址。 二. python安装步骤 滑动到红色框内 Downloads 导航栏。 红色框是选择适合自己电脑系统和版本的部分&#xff0c;蓝色框是选择系统的部分&#xff0c;黄色框是版本号。…

【大数据】数据分析之Spark框架介绍

文章目录 概述一、发展历程与背景二、核心特点三、生态系统与组件四、应用场景五、与其他大数据技术的比较 核心概念1. 弹性分布式数据集&#xff08;RDD, Resilient Distributed Dataset&#xff09;2. 转换&#xff08;Transformations&#xff09;和动作&#xff08;Actions…

Rust编程的函数

【图书介绍】《Rust编程与项目实战》-CSDN博客 《Rust编程与项目实战》(朱文伟&#xff0c;李建英)【摘要 书评 试读】- 京东图书 (jd.com) Rust编程与项目实战_夏天又到了的博客-CSDN博客 7.1 函 数 定 义 在Rust中&#xff0c;函数使用fn关键字定义&#xff0c;后跟函数…

how to increase the height of the ps or cdm window

when the line reaches the bottom; directly pull up the top bar of the window after pulling down the bar

【Linux】ComfyUI和SD WebUI之PYTHON环境共享,模型共享,LORA等公共资源共享

需求 一般玩AI绘图都会装ComfyUI和SD WebUI。而且这俩的模型、lora等都是一致的。为了避免空间的浪费&#xff0c;一般会采用共享数据的方式。而且共享的数据可以任意指定分区&#xff0c;这让挂载NAS共享空间成为可能&#xff0c;实现多绘画机ComfyUI和SD WebUI共享资源。 实…

攀爬数据集,约500张 !VOC格式,yolo可直接使用~真实场景特征明显高清图,yolo可直接使用!

攀爬数据集&#xff0c;约500张&#xff01;&#xff01;&#xff01; VOC格式&#xff0c;yolo可直接使用&#xff5e; 真实场景特征明显高清图&#xff0c;yolo可直接使用&#xff01; 攀爬数据集&#xff0c;约500张&#xff01;&#xff01;&#xff01; VOC格式&#xff0…

用GPT-4o打造LLM+OS(10+实用技能),代码开源,指令曝光,科技演示惊艳全场!

目录 前言 LLM操作系统能力概况&#xff08;phidata中前5个已经实现&#xff09;&#xff1a; 可以读取/生成文本 拥有比任何单个人类更全面的知识 可以浏览互联网 可以使用现有的软件基础设施&#xff08;计算器、Python、鼠标/键盘&#xff09; 可以与其他LLMs通信 可…

一文了解,ARM 工业计算机的发展历程

ARM 工业计算机的发展历程主要经历了以下几个阶段&#xff1a; 早期探索阶段&#xff08;20 世纪 80 年代 - 90 年代初&#xff09;&#xff1a; 起源背景&#xff1a;20 世纪 80 年代&#xff0c;计算机工业蓬勃发展&#xff0c;英国的 Acorn 公司在这一时期积极探索芯片技术…

Unity实现自定义图集(四)

以下内容是根据Unity 2020.1.0f1版本进行编写的   在之前的篇章中已经把自定义图集在编辑器上的使用,以及运行时所需的信息都准备好了,接下来就是魔改UGUI的Image组件,使其能够像Image那样运行时如果引用的资源有打自定义图集,则加载对应自定义图集的Texture。 1、思路 …

Centos7通过jengkins实现自动发布和回滚

一、安装jenkins 注&#xff1a;这里不多说哈&#xff0c;百度遍地都是&#xff0c;安装方式不限。 二、jenkins创建项目 注&#xff1a;这里有个坑需要说一下&#xff0c;最开始我使用的是maven构建&#xff0c;但是如果按照我的这套方案会有一个编译死循环的问题&#xff0c;…

【Linux】多进程服务器模型(第十九篇)

目录 一、定义与工作原理 二、特点与优势 三、实现与示例 四、注意事项 多进程服务器模型是一种在服务器端使用的并发处理模型&#xff0c;它允许服务器同时处理多个客户端的请求。以下是关于多进程服务器模型的详细介绍&#xff1a; 一、定义与工作原理 定义&#xff1a;…

抽象类Abstart Class

抽象类其实就是一种不完全的设计图 必须用abstract修饰 模板方法&#xff1a;建议使用final修饰&#xff0c;不能被重写。

提高ROI:低代码平台如何助力企业实现成本效益最大化

引言&#xff1a;成本效益与ROI的重要性 在当今竞争异常激烈的商业环境中&#xff0c;企业面临着前所未有的挑战。如何在有限的资源下&#xff0c;最大化投资回报率&#xff08;ROI&#xff09;&#xff0c;已经成为企业管理者不可忽视的关键课题。ROI不仅仅是衡量投资回报的指…

PROFINET 转 EtherCAT, EtherCAT/Ethernet/IP/Profinet/ModbusTCP协议互转工业串口网关

EtherCAT/Ethernet/IP/Profinet/ModbusTCP协议互转工业串口网关https://item.taobao.com/item.htm?ftt&id822721028899 协议转换通信网关 PROFINET 转 EtherCAT GW系列型号 MS-GW31 概述 简介 MS-GW31 是 PROFINET 和 EtherCAT 协议转换网关&#xff0c;为用户提供两…

服装生产管理的数字化转型:SpringBoot框架

4 系统设计 4.1 系统结构设计 在结构设计过程中&#xff0c;首先对系统进行需求分析&#xff0c;然后进行系统初步设计&#xff0c;将系统功能模块细化&#xff0c;具体分析每一个功能模块具体应该首先哪些功能&#xff0c;最后将各个模块进行整合&#xff0c;实现系统结构的…

Javascript动态规划算法

JavaScript中的动态规划&#xff08;Dynamic Programming&#xff0c;简称DP&#xff09;是一种通过把原问题分解为相对简单的子问题的方式来求解复杂问题的方法。它主要致力于将“合适”的问题拆分成更小的子目标&#xff0c;并通过建立状态转移方程、缓存并复用以往结果以及按…