• <C++>map 容器快速上手|自定义数据类型排序的避坑理解


    ✅作者简介:热爱后端语言的大学生,CSDN内容合伙人
    ✨精品专栏:C++面向对象
    🔥系列专栏:C++泛型编程


    🔥前言

    set 容器后,今天总结一下 map 容器的功能,从零到一快速掌握基本使用与常用接口。mapSTL 编程中与 vectorlistset 具有同等重要的地位,键值对的方式存储元素在查找时很是高效,那么下面正式开始 map 容器的学习之旅。

    1、map 容器基本操作,从构造到查找统计

    1.1、map/ multimap 基本概念

    特点:

    • map中所有元素都是二元组pair
    • 二元组中第一个元素为key(键),起到索引作用,第二个元素为value(值)
      • 优点:可以根据key值快速找到value值
    • 所有元素都会根据元素的键值自动升序排序

    本质:

    • map/multimap属于关联式容器,底层结构是用二叉树实现

    二者区别:

    • map不允许容器中有重复key值元素
    • multimap允许容器中有重复key值元素

    1.2、map 赋值和构造

    功能:

    • 对map容器进行构造和赋值操作

    函数原型:

    • map mp; 默认构造函数:
    • map(const map &mp); 拷贝构造函数
    • map& operator=(const map &mp); 重载等号操作符

    代码示例:

    // 遍历容器
    void printInfo(map<int,int>& mp)
    {
    	for (map<int, int>::iterator it = mp.begin(); it != mp.end(); it++) {
    		cout << "key = " << it->first << " value = " << it->second << endl;
    	}
    	cout << endl;
    }
    
    // 构造和赋值
    void test01()
    {
    	// 默认构造
    	map<int, int> mp1;
    	// 利用二元组显示创建
    	mp1.insert(make_pair(1,1));
    	mp1.insert(make_pair(5,2));
    	mp1.insert(make_pair(4,5));
    	printInfo(mp1);
    	// 拷贝构造
    	map<int, int> mp2(mp1);
    	printInfo(mp2);
    	// 赋值
    	map<int, int> mp3;
    	mp3 = mp2;
    	printInfo(mp3);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    1.3、map 大小和交换

    功能:

    • 统计map容器大小以及交换map容器

    函数原型:

    • size(); 返回容器中元素的数目
    • empty(); 判断容器是否为空
    • swap(st); 交换两个集合容器

    代码示例:

    // 大小和交换
    void test02()
    {
    	// 大小
    	map<int, int>mp;
    	mp.insert(pair<int, int>(1, 10));
    	mp.insert(pair<int, int>(3, 30));
    	mp.insert(pair<int, int>(2, 20));
    
    	if (mp.empty())
    	{
    		cout << "m为空" << endl;
    	}
    	else
    	{
    		cout << "m的大小为: " << mp.size() << endl;
    	}
    
    	// 交换
    	map<int, int>m2;
    	m2.insert(pair<int, int>(4, 100));
    	m2.insert(pair<int, int>(5, 200));
    	m2.insert(pair<int, int>(6, 300));
    
    	cout << "交换前" << endl;
    	printInfo(mp);
    	printInfo(m2);
    
    	cout << "交换后" << endl;
    	mp.swap(m2);
    	printInfo(mp);
    	printInfo(m2);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    1.4、map 插入和删除

    功能:

    • map容器进行插入数据和删除数据

    函数原型:

    • insert(elem); 在容器中插入元素。
    • clear(); 清除所有元素
    • erase(pos); 删除pos迭代器所指的元素,返回下一个元素的迭代器。
    • erase(beg, end); 删除区间[beg,end)的所有元素 ,返回下一个元素的迭代器。
    • erase(key); 删除容器中值为key的元素。

    代码示例:

    // 插入和删除
    void test03()
    {
    	//插入操作
    	map<int, int> mp;
    	//第一种插入方式
    	mp.insert(pair<int, int>(1, 10));
    	//第二种插入方式,推荐
    	mp.insert(make_pair(2, 20));
    	//第三种插入方式
    	mp.insert(map<int, int>::value_type(3, 30));
    	//第四种插入方式,不推荐:当不存在此值就会自动创建键,值为0
    	mp[4] = 40;
    	// 模拟误操作
    	cout << "下标为6的值为:"<<mp[6] << endl;
    	printInfo(mp);
    
    	//删除,按迭代器
    	mp.erase(mp.begin());
    	printInfo(mp);
    	// 删除,按下标
    	mp.erase(3);
    	mp.erase(6);
    	printInfo(mp);
    
    	//清空
    	mp.erase(mp.begin(), mp.end());
    	mp.clear();
    	printInfo(mp);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    1.5、map 查找和统计

    功能:

    • 对map容器进行查找数据以及统计数据

    函数原型:

    • find(key); 查找key是否存在,若存在,返回该键对应的迭代器;若不存在,返回end();
    • count(key); 统计key的元素个数

    代码示例:

    // 查找和统计
    void test04()
    {
    	map<int, int>mp;
    	mp.insert(pair<int, int>(1, 10));
    	mp.insert(pair<int, int>(2, 20));
    	mp.insert(pair<int, int>(4, 40));
    	mp.insert(pair<int, int>(3, 30));
    
    	//查找
    	map<int, int>::iterator pos = mp.find(3);
    
    	if (pos != mp.end())
    	{
    		cout << "找到了元素 key = " << (*pos).first << " value = " << (*pos).second << endl;
    	}
    	else
    	{
    		cout << "未找到元素" << endl;
    	}
    
    	//统计
    	int num = mp.count(2);
    	cout << "num = " << num << endl;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    2、自定义排序规则

    set 容器一样,定义排序规则需要在创建容器的时候就指定,同样需要借助仿函数,也就是后面会提到的 谓词

    2.1、内置数据类型的排序

    // 内置数据类型排序
    class descmp
    {
    public:
    	bool operator()(int val1, int val2) const {
    	    // 指定为降序排列 
    		return val1 > val2;
    	}
    };
    void test05()
    {
    	cout << "降序排序插入:" << endl;
    	map<int, int,descmp> mp;
    	mp.insert(make_pair(3, 2));
    	mp.insert(make_pair(7, 4));
    	mp.insert(make_pair(6, 1));
    	mp.insert(make_pair(8, 5));
    	mp.insert(make_pair(1, 3));
    
    	for (auto it = mp.begin(); it != mp.end(); it++) {
    		cout << "key = " << it->first << " value = " << it->second << endl;
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    在这里插入图片描述

    仿函数需要指定为 const 常函数类型,否则将会出现数据不完整错误

    2.2、自定义数据类型的排序

    进行自定义数据类型排序的时候要注意,使用仿函数定义排序规则是针对 的,因此要把定义的类、结构体放在泛型的第一个参数上(别问,问就是我看了半天源码才发现的…)

    接下来看看我设计的小案例:

    // 自定义数据类型排序
    class Hero
    {
    public:
    	// 构造方法
    	Hero(string name, int age) :name(name), age(age) {}
    	// 属性
    	string name;
    	int age;
    };
    class Cmp_hero
    {
    public:
    	bool  operator()(const Hero& h1, const Hero& h2)const {
    		return h1.age < h2.age;
    	}
    };
    void test06()
    {
    	map<Hero, int, Cmp_hero> mh;
    	Hero h1("赵云", 42);
    	Hero h2("曹操", 43);
    	Hero h3("孙策", 39);
    	Hero h4("刘备", 40);
    	Hero h5("关羽", 41);
    	mh.insert(make_pair(h1, 12000));
    	mh.insert(make_pair(h2, 14000));
    	mh.insert(make_pair(h3, 10000));
    	mh.insert(make_pair(h4, 18000));
    	mh.insert(make_pair(h5, 16000));
    
    	for (auto t = mh.begin(); t != mh.end(); t++) {
    		cout << "姓名:" << t->first.name << " 年龄:" << t->first.age
    			<< " 赏金:" << t->second << endl;
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    在这里插入图片描述

    其实更合理的设计应该是按照赏金大小来排序,我之所以这样设计是想介绍一下当键是类的时候该怎么处理:仿函数参数列表中的变量若是 引用形式 必须用 const 修饰(若不是引用形式则不用加 const),而且函数也应该是常函数。


    map 容器键值对存储的使用是非常广泛的,下篇博客来做一个员工分组的具体案例,巩固一些 STL常用容器的使用,大家可以订阅专栏,方便查阅和复习。

  • 相关阅读:
    Linux网络通信优化方法
    制氢厂氢气泄漏安全监测:氢气传感器守护“氢”安全
    关于Elasticsearch(es)中使用sql返回结果只有1000条
    ubuntu下ipynb文件转py文件
    使用Docker开发GO应用程序
    实战分析:项目业务模块划分与包名设计思想
    vue3+vite3+vant搭建移动端简易模版
    CocosCreator3.8研究笔记(十四)CocosCreator 资源管理Asset Manager
    VUE项目使用.env配置多种环境以及如何加载环境
    leetcode16最接近的三数之和 (排序+ 双指针)
  • 原文地址:https://blog.csdn.net/m0_58618795/article/details/127696201