【C++进阶】Map 和 Set(详解)

news2024/11/17 21:32:10

🌈欢迎来到C++专栏~~Map 和Set


  • (꒪ꇴ꒪(꒪ꇴ꒪ )🐣,我是Scort
  • 目前状态:大三非科班啃C++中
  • 🌍博客主页:张小姐的猫~江湖背景
  • 快上车🚘,握好方向盘跟我有一起打天下嘞!
  • 送给自己的一句鸡汤🤔:
  • 🔥真正的大师永远怀着一颗学徒的心
  • 作者水平很有限,如果发现错误,可在评论区指正,感谢🙏
  • 🎉🎉欢迎持续关注!
    在这里插入图片描述

请添加图片描述

Map 和Set

  • 🌈欢迎来到C++专栏~~Map 和Set
    • 一. 关联式容器
    • 二. 键值对
    • 三. C++中的Set
      • 1️⃣Set的介绍
      • 2️⃣Set的使用(参照文档)
        • 🌈set的模板参数列表
        • 🌈set的构造
        • 🌈set的迭代器
        • 🌈set的常见修改操作
      • 3️⃣Multiset的用法
    • 四. C++中的Map
      • ⚡Map的介绍
      • ⚡Map的用法
        • 💦Map的模板参数
        • 💦Map的迭代器
        • 💦Map的构造
        • 💦Map的常见修改操作
        • 💦Map的[ ]的使用(重头戏)
      • ⚡统计次数的两种方法
      • ⚡multimap的用法
    • 五. 来两道题目练练手
      • 1️⃣前K个高频单词
      • 2️⃣两个数组的交集
  • 📢写在最后

请添加图片描述

一. 关联式容器

在初阶阶段,我们已经接触过STL中的部分容器,比如:vector、list、deque、
forward_list(C++11)等,这些容器统称为序列式容器,因为其底层为线性序列的数据结构,里面存储的是元素本身。那什么是关联式容器?它与序列式容器有什么区别?

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

关联式容器容器结构底层实现
set、map、multiset、multimap树型结构平衡搜索树(红黑树)
unordered_set、unordered_map、unordered_multiset、unordered_multimap哈希结构哈希表,哈希桶

二. 键值对

用来表示具有一一对应关系的一种结构,该结构中一般只包含两个成员变量key和value,key代表键值value表示与key对应的信息

举例子:

  • 现在要建立一个英汉互译的字典,那该字典中必然有英文单词与其对应的中文含义,而且,英文单词与其中文含义是一一对应的关系,即通过该应该单词,在词典中就可以找到与其对应的中文含义

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

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

三. C++中的Set

1️⃣Set的介绍

Set本质就是Key的模型,就是确认该值在不在

2️⃣Set的使用(参照文档)

Set支持的操作是增删查,不能改!(因为底层是一颗搜索树,改了就乱了)

🌈set的模板参数列表

在这里插入图片描述

三个模板参数:

  • T:set中存放元素的类型,实际在底层存储<value, value>的键值对
  • Compare仿函数;set中元素默认按照小于来比较(如果比较的方式不满意,可以自己去控制比较规则:自己写仿函数)
  • Alloc:set中元素空间的管理方式,使用STL提供的空间配置器内存池

注意:在使用set时,需要包含头文件set

🌈set的构造

在这里插入图片描述

拷贝都是深拷贝,要拷贝一棵树,代价很大

void testset2()
{
	set<int> set1;//空构造
	int num[] = { 1, 2, 3, 7, 9, 3, 10, 1 };

	//区间构造
	set<int> set2(num, num + sizeof(num) / sizeof(num[0]));

	//拷贝构造
	set<int> set3(set2);

	for (auto& e : set3) //打印结果看出set可以去重
	{
		cout << e << " ";
	}
	cout << endl;
}

在这里插入图片描述

🌈set的迭代器

此处和之前的容器差不多,所以就省略过,直接看图

函数说明功能介绍
begin + end(重点)获取第一个数据位置的, 获取最后一个数据下一个位置
cbegin() const 和 cend()const无非就是针对const的版本,不可写
rbegin + rend获取最后一个数据位置,获取第一个数据前一个位置
反向迭代器同理

在这里插入图片描述

void test_set()
{
	//set<int> s;
	//s.insert(1);
	//s.insert(2);
	//s.insert(3);

	set<int> s = { 1, 2, 1, 6, 3, 8, 5 };
	//排序 + 去重
	set<int>::iterator it = s.begin();
	while (it != s.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;

	//支持迭代器也就支持范围for了
	for (auto e : s)
	{
		cout << e << " ";
	}
	cout << endl;
}

在这里插入图片描述

如果我要整成降序的呢?

  1. 反向迭代器
  2. 仿函数: less—>greater
	int a[] = { 1, 2, 1, 6, 3, 8, 5 };
	set<int, greater<int>> s(a, a + sizeof(a) / sizeof(int));

🌈set的常见修改操作

🥑find & erase

在这里插入图片描述

普通的寻找和删除当然没有什么问题!
但是特殊的呢?

  • 我删除一个不存在的值呢?会怎么样??

所以写完find要判断一下是否真正的找到了,在才删除

	set<int>::iterator pos = s.find(20);
	if (pos != s.end())
	{
		s.erase(pos);
	}

🥑count

在这里插入图片描述

在这里插入图片描述

但是count不是为此设计的!是为了和multiset保持接口的一致性,所以都设计了

提一嘴
🥑lower_bound 和 upper_bound

我想把某个范围的值都找到!

  • lower_bound下限的意思,返回 >= val的值
  • upper_bound上限的意思,返回 > val的值
	itlow = myset.lower_bound(35);//返回大于等于35的值   
	itup = myset.upper_bound(60);//返回大于60的值

在这里插入图片描述

3️⃣Multiset的用法

multiset容器与set容器实现和接口基本一致,唯一区别就是,multiset 允许键值冗余,即multiset容器当中存储的元素是可以重复的

代码展示:

void testset4()
{
	int a[] = { 1, 2, 1, 6, 3, 8, 5 };
	//允许键值冗余
	multiset<int> s(a, a + sizeof(a) / sizeof(int));
	
	for (auto e : s)
	{
		cout << e << " ";
	}
	cout << endl;
}

结果展示:

在这里插入图片描述

键值冗余之后,我们的find该怎么样找数据呢?

  1. find时,如果有多个值,返回中序的第一个
void testset4()
{
	int a[] = { 1, 2, 1, 6, 3, 8, 5, 3};
	//允许键值冗余
	multiset<int> s(a, a + sizeof(a) / sizeof(int));
	
	//find时,如果有多个值,返回中序的第一个
	auto pos = s.find(3);
	while (pos != s.end())
	{
		cout << *pos << " ";
		++pos;//这里的++ ,就是加到下一个的3的位置
	}
	cout << endl;
}

在这里插入图片描述

当然如果是要删除erase3的时候,肯定是删除所有的3!

四. C++中的Map

⚡Map的介绍

  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的用法

💦Map的模板参数

在这里插入图片描述

参数说明:

  1. key: 键值对中key的类型

  2. T: 键值对中value的类型

  3. Compare: 比较器的类型,map中的元素是按照key来比较的,缺省情况下按照小于来比较,一般情况下(内置类型元素)该参数不需要传递,如果无法比较时(自定义类型),需要用户自己显式传递比较规则(一般情况下按照函数指针或者仿函数来传递)

  4. Alloc:通过空间配置器来申请底层空间,不需要用户传递,除非用户不想使用标准库提供的空间配置器

使用map要包含map的头文件哦

💦Map的迭代器

	//map<string, string>::iterator it = dict.begin();
	auto it = dict.begin();

	while (it != dict.end())
	{
		//pair没有重载流插入
		//cout << (*it).first << (*it).second << endl;
		//cout << it->->first << it->->second << endl; 
		//operator重载
		cout << it->first << it->second << endl;

		++it;
	}
	
	for (const auto& kv : dict)//给const & 引用
	{
		cout << kv.first << ":" << kv.second << endl;
	}
	cout << endl;

💦Map的构造

在这里插入图片描述

void testmap1()
{
	map<int, int> map1;//空构造
	int num[] = { 1,5,9,4,8,2,3,1,5,4,5,7 };
	for (auto e : num)
	{
		map1.insert(make_pair(e,e));
	}
	map<int, int> map2(map1.begin(),map1.end());//迭代区间构造
	map<int, int> map3(map2);//拷贝构造
	for (auto& e : map3)
	{
		cout << e.first << ":" << e.second << endl;
	}
}

💦Map的常见修改操作

🌏Insert

在这里插入图片描述
find插入的是pair的结构体,此版本是不支持冗余的,插入只看key值,有相等的就不需要了

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

	//匿名对象
	dict.insert(pair<string, string>("sort", "排序"));
	dict.insert(pair<string, string>("test", "测试"));
	dict.insert(pair<string, string>("string", "字符串"));

	//宏替换
	typedef pair<string, string> Dictkv;
	dict.insert(Dictkv("string", "字符串"));
}

还有一个很便捷的操作:make_pair:构造匿名的pair,返回

	dict.insert(make_pair("string", "字符串"));//很常用

在这里插入图片描述

💦Map的[ ]的使用(重头戏)

在这里插入图片描述

[]:的功能:查找 + 修改

  • map中有这个key,就返回value引用(查找 + 修改)
  • map中没有这个key,会插入一个pair(key, K()),会插入这个key值,value就要去调用默认构造,最后还是返回value的引用 (插入+ 修改)

如果是int,就是0;是指针,就默认空指针;

	map<string, string> dict;
	dict.insert(make_pair("sort", "排序"));
	dict["insert"];                       //插入
	dict["insert"] = "插入";              //插入 + 修改value返回值
	dict["left"] = "左边";                //插入 + 修改    

在这里插入图片描述

可以看见Insert的返回值和实现

在这里插入图片描述

  • key已经在map中,返回pair(key_iteratorfalse
  • key不在map中,返回pair(new_key_iteratortrue

[]其底层实现:有两个pair结构

  • ✨返回值是一个pair结构pair<iterator, bool>;第一个值是迭代器,第二个值是bool;第二个bool是用来反应是否插入成功,如果成功,则 第一个值迭代器就是指向插入的pair数据
  • 第二个是kv的pair,是插入的pair数据
V& operator[](const K& key)
{
	pair<iterator, bool> ret = insert(make_pair(key, V());
	//返回key节点的迭代器,迭代器是map的,再调用->就是pair*,取second
	return ret.first->second;
}

⚡统计次数的两种方法

  1. 第一次找不到的时候make_pair,使得count = 1
  2. 后面每遍历一次就++一次
//统计次数
void test_map2()
{
	string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜","苹果", "香蕉", "苹果", "香蕉" };
	map<string, int> countMap;
	for (auto& str : arr)
	{
		map<string, int>::iterator it = countMap.find(str);
		if (it != countMap.end())
		{
			/*(*it).second++;*/
			//it->->second++;  第一个箭头是调用operator->返回的是pair*,再->指向成员
			it->second++;
		}
		else
		{
			//找不到
			countMap.insert(make_pair(str, 1));
		}
	}

	//遍历
	map<string, int>::iterator it = countMap.begin();
	while(it != countMap.end())
	{
		cout << it->first << ":" << it->second << endl;
		++it;
	}
	cout << endl;
}

在这里插入图片描述

第二种方法[]

	map<string, int> countMap;
	for (auto& str : arr)
	{
		//1、str不在countMap中,插入pair(str, int()),然后对返回次数++
		//2、str在的话,就返回value的引用次数++
		countMap[str]++;
	}

在这里插入图片描述

⚡multimap的用法

multimap容器与map容器的底层实现以及成员函数的接口都是基本一致,区别是multimap 允许键值冗余,即multimap容器当中存储的元素是可以重复的

	multimap<string, string> mdict;
	mdict.insert(make_pair("sort", "排序"));
	mdict.insert(make_pair("left", "左边"));
	mdict.insert(make_pair("left", "左边"));
	mdict.insert(make_pair("left", "你猜"));

在这里插入图片描述

五. 来两道题目练练手

1️⃣前K个高频单词

题目地址:传送

在这里插入图片描述

要注意要按出现次数按大到小的来,如果出现次数相等,要按字母顺序排序,比如:ilove都出现了两次,那么 按字母顺序i要在love之前

思路:

  1. 使用一个countMap统计各种单词的出现的次数(统计次数 此时是i在上面,love在下面)
  2. 再写一个sortMap按照字符出现的次数进行降序排列
  3. 不同的单词出现相同的次数,会按字典序排列

在这里插入图片描述
注意不能用反向迭代来进行,不然同样的次数,love会在前面,i在后;就违背了题意

class Solution {
public:
    vector<string> topKFrequent(vector<string>& words, int k) {
        map<string, int> countMap;
        //统计次数   此时是i在上面,love在下面
        for(auto& str : words)
        {
            countMap[str]++;   
        }

        // apple 3  \  i  2  \  love  2 \ j  5
        multimap<int, string, greater<int>> sortMap;//排降序
        for(auto& kv : countMap)
        {
            sortMap.insert(make_pair(kv.second, kv.first)); 
        }

        vector<string> v;
        multimap<int, string, greater<int>> :: iterator it = sortMap.begin();

        for(size_t i = 0; i < k; i++)
        {
            v.push_back(it->second);
            it++;
        }
        return v;
    }
};

2️⃣两个数组的交集

题目地址:传送

在这里插入图片描述

思路:
请添加图片描述

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

        set<int> :: iterator it1 = s1.begin();
        auto it2 = s2.begin();

        vector<int> v;
        while(it1 != s1.end() && it2 != s2.end())
        {
            if(*it1 < *it2)
            {
                ++it1;
            }
            else if(*it1 > *it2)
            {
                ++it2;
            }
            else
            {
                v.push_back(*it1);
                ++it1;
                ++it2;
            }
        }
        return v;
    }
};

那如果是求差集呢?

在这里插入图片描述

📢写在最后

在这里插入图片描述

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

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

相关文章

【JavaScript】DOM 的概念、获取DOM元素和操作元素属性

文章目录【JavaScript】DOM 的概念、获取DOM元素和操作元素属性一. 概念二. DOM 操作(1) 获取DOM元素的方式1. document 获取元素方法2. 通过 HTML5 新增的方法获取案例&#xff1a;登录界面密码显示与隐藏(2) 读写元素内部的结构内容1. 改变元素节点里的内容2. 改变元素节点的…

操作系统的基本概念、功能、目标

文章目录&#x1f380;前言&#xff1a;本篇博客知识总览&#x1fa82;操作系统所处位置&#x1f4d6;操作系统的概念&#x1f3af;操作系统的功能和目标&#x1fa85;1.操作系统作为系统资源的管理者&#x1fa85;2.操作系统作为用户与计算机硬件之间的接口&#x1fa85;3.操作…

连接数据库和简单操作数据库

连接数据库和简单操作数据库JDBC程序编写步骤创建一个演员表数据库表的连接前置工作五种连接方式方式五的配置文件配置文件里面的内容通过JDBC进行对actor表操作。ideal执行后的结果数据库actor表结果JDBC程序编写步骤 1.注册驱动-加载Driver类 2.获取连接-得到Connection 3.执…

【阶段一】Python快速入门04篇:运算符、循环语句、条件语句与函数

本篇的思维导图: 运算符 算术运算符 算术运算就是常规的加、减、乘、除类运算。下表为基本的算术运算符及其示例。 描述 代码

【C++常用容器】STL基础语法学习map容器

目录 ●map基本概念 ●map构造和赋值 ●map大小和交换 ●map插入和删除 ●map查找和统计 ●map排序&#xff08;map初始排序顺序为从小到大&#xff0c;用仿函数将其改为从大到小&#xff09; ●map基本概念 map中的所有元素都是pair&#xff0c;pair中第一个元素为key&a…

【WeThinkIn出品】2022年度总结

Rocky Ding公众号&#xff1a;WeThinkIn写在前面 【WeThinkIn出品】栏目专注于分享Rocky的最新思考与经验总结&#xff0c;包含但不限于技术领域。欢迎大家一起交流学习&#x1f4aa; 这篇文章发布的时候&#xff0c;应该已经是2023年了。在这里Rocky祝大家元旦快乐&#xff01…

前端最常用的几个线上设计网站

文章目录前言CoDesign 腾讯自研设计平台【墙裂推荐】蓝湖- 高效的产品设计协作平台【墙裂推荐】zeplin Deliver on the promise of design 【国外&#xff0c;不推荐】总结前言 随着IT技术的不断进步&#xff0c;很多团队都将很对线下工作转移到了线上&#xff0c;不仅便捷&a…

Ae 效果详解:毛边

Ae菜单&#xff1a;效果/风格化/毛边Effects/Stylize/Roughen Edges毛边 Roughen Edges效果可使得 Alpha 通道的边缘变粗糙&#xff0c;可以为图像添加各种边缘效果。通过分形影响改变边缘样式&#xff0c;并可增加颜色以模拟铁锈和其他类型的腐蚀。此效果可为文本或图形提供自…

算法之数组常见题目

数组是存储在连续内存空间上的相同类型数据的集合。在数组中可以方便地通过下标索引的方式获取对应的数据。 需要注意的是&#xff1a; 数组的下标都是从0开始的。数组在内存空间是连续的&#xff0c;所以删除或者增添元素时难免要移动其他元素的地址&#xff0c;只能覆盖。 …

【Kubernetes | Pod/容器】如何修改 Pod 中容器的守护进程

目录题1. 环境设定1.1 创建名为 vmware-nginx 的 Pod2. 查看容器默认守护进程2.1 查看容器所在节点2.2 查看容器ID2.3 查看容器中运行的进程信息3. 修改容器默认守护进程3.1 类比 Docker3.2 修改 YAML 文件改变默认守护进程参数说明4. 验证4.1 删除旧的 vmware-nginx.yaml 容器…

质性分析软件nvivo的学习(一)

1、软件安装&#xff1a; 科研也是需要投资的&#xff0c;建议淘宝购买软件,价格60米。 2、软件基础使用说明&#xff1a; 说明&#xff1a;以下笔记来源都是通过B站视频自学总结的&#xff0c;您可以选择通过下面的B站视频学习&#xff0c;也可以选择通过我总结的内容速学。…

(Week 9)图论(C++,Dijkstra,Floyd)

文章目录【深基18.例3】查找文献&#xff08;C&#xff0c;图的遍历&#xff09;题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1解题思路&#xff1a;【模板】floyd&#xff08;C&#xff09;题目背景题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1样例 #2样…

内核内存管理

1.内核内存管理框架 内核将物理内存等分成N块4KB&#xff0c;称之为一页&#xff0c;每页都用一个struct page来表示&#xff0c;采用伙伴关系算法维护 内核地址空间划分图&#xff1a; 3G~3G896M&#xff1a;低端内存&#xff0c;直接映射 虚拟地址 3G 物理地址 细分为&a…

我国软件供应链安全现状学习分析整理分享 附完整下载地址

声明 本文是学习2021年中国软件供应链安全分析报告. 下载地址而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 一、前言 数字化时代&#xff0c;软件无处不在。软件如同社会中的“虚拟人”&#xff0c;已经成为支撑社会正常运转的最基本元素之一&#…

MATLAB | 绘图复刻(五) | 带树状图的环形热图

本期教大家如何绘制带树状图的环状热图&#xff0c;要复刻的图片长这样&#xff1a; 复刻效果&#xff1a; 需要安装Statistics and Machine Learning Toolbox即统计与机器学习工具箱&#xff01;&#xff01;&#xff01; 需要安装Statistics and Machine Learning Toolbox即…

对DataFrame的数据进行指定运算的DataFrame.transform()方法

【小白从小学Python、C、Java】 【计算机等级考试500强双证书】 【Python-数据分析】 对DataFrame中所有数据实施指定运算 例如&#xff1a;df1.transform(np.abs) 选择题 关于以下python代码说法错误的一项是? import pandas as pd import numpy as np df pd.DataFrame({&…

上班摸鱼软件

上班摸鱼软件前言思路采集组合自动下单发QQ报告结语前言 疫情几年&#xff0c;在家时间多&#xff0c;上班时间少。没事多在网上闲逛&#xff0c;偶然在知乎上看到一篇文章&#xff0c;说是买球也能稳赚不赔。我研究了一下&#xff0c;还真是这么回事。 简单来说&#xff0c;就…

转换通达信分钟数据,包括5分钟和1分钟数据

目录 1 前言 2 操作演示 3 代码 4 软件下载 5 stockpy整体功能介绍 1 前言 真正的市场高手不但要熟练掌握日线&#xff0c;对分钟线也要进行深入研究。缠中说禅在他的博客中讲到&#xff0c;年、季、月、周、日、60分钟、30分钟、5分钟、1分钟研究道理是相同的。粒度越细&…

华为云之ModelArts+AppCube带你识别101种西式美食

目录 1.注册并实名认证华为云账号 2.AI Gallery 订阅模型及部署 3. 获取访问秘钥 4.使用示例安装包创建 AppCube 应用 5. 创建 ModelArts 连接器 6. 应用修改 6.1 修改脚本 6.2 修改标准页面 6.3 运行及测试 1.注册并实名认证华为云账号 未注册及实名认证的话&…

2023年1月1日生效:2023年火车高铁儿童票最新规则及高铁火车2023儿童票怎么购买?

原文来源&#xff1a;https://www.caochai.com/article-4108.html 2023年火车高铁儿童票最新规则&#xff1a; 自2023年1月1日起生效&#xff0c;儿童购买高铁火车票将执行最新规则&#xff1a;按购票儿童年龄执行票价阶梯。 1、【免费】儿童年龄小于6周岁&#xff0c;无需购…