STL | Map(映射)的使用
Map是STL的一个关联容器,它提供一对一(其中第一个可以称为关键字,每个关键字只能在map中出现一次,第二个可能称为该关键字的值)的数据处理能力。由于这个特性,它完成有可能在我们处理一对一数据的时候,在编程上提供快速通道。
map内部自建一颗红黑树(一 种非严格意义上的平衡二叉树),这颗树具有对数据自动排序的功能,所以在map内部所有的数据都是有序的。
一. 定义一个Map(映射)
map共提供了6个构造函数,介绍最常见的一种构造方法:
#include<map>// 头文件
map<char,int>Map;// 定义一个从char类型到int类型的映射Map
构造函数包含了两个参数,第一个参数是关键字Key,第二个参数是关键字的值Key_Value。这两个参数可以是任意类型。
通常,我们习惯用typedef来定义Map,为什么呢?因为在定义迭代器的时候,我们可以不用多写多余的重复代码,而且要修改Map的时候,也不需要修改多处地方。
#include<map>
typedef map<char,int> m;// 自定义一种类型——m
m Map;
二. 插入数据
定义好了映射Map,接下来就需要向Map插入数据。插入数据主要有以下三种方法。
① 用insert函数插入pair数据(pair是什么,其实就是一个结构体,包含(first,second)这样的数据的结构体));
char ch = “a”;
int i = 10;
Map.insert(pair<char,int>(ch,i));
② 用insert函数插入value_type数据;
Map.insert(m::value_type(ch,i));
③ 用数组方式插入数据:形式如:Map[Key]=Key_Value;
Map[ch]=i;
以上三种插入方法,虽然都可以实现数据的插入,但是它们是有区别的。当然,第一种和第二种在效果上是完成一样的。
用insert函数插入数据,在数据的插入上涉及到集合的唯一性这个概念,即当map中有这个关键字时,想要再次插入该关键字,insert操作是插入不了的。
但是用数组方式就不同了,它可以覆盖以前该关键字对应的值。
三. Map容器的大小
Map容器已经插入了数据,那么Map容器的大小(目前)是多大呢?或者说插入了多少组数据呢?
Map容器已经插入了数据,那么Map容器的大小(目前)是多大呢?或者说插入了多少组数据呢?
四. 遍历Map容器
既然插入了数据,那么我们怎么去遍历呢?(借此也看看是不是插入成功)同样的,也有三种方法可以对Map容器进行遍历。
① 前向迭代器遍历
map<char,int>::iterator iter;// 前向迭代器
for(iter=Map.begin();iter!=Map.end();iter++)
{
cout<<iter->first<<" "<<iter->second<<endl;
}
② 反向迭代器遍历:所谓反向,就是从末尾开始遍历。
map<char,int>::iterator iter;// 前向迭代器
for(iter=Map.begin();iter!=Map.end();iter++)
{
cout<<iter->first<<" "<<iter->second<<endl;
}
③ 数组的形式遍历
#include <map>
#include <string>
#include <iostream>
using namespace std;
int main() {
map<int, string> mapStudent;
mapStudent.insert(pair<int, string>(1, "student_one"));
mapStudent.insert(pair<int, string>(2, "student_two"));
mapStudent.insert(pair<int, string>(3, "student_three"));
int nSize = mapStudent.size();
//此处应注意,应该是 for(int nindex = 1; nindex <= nSize; nindex++)
//而不是 for(int nindex = 0; nindex < nSize; nindex++)
for(int nindex = 1; nindex <= nSize; nindex++)
cout<<mapStudent[nindex]<<endl;
}
五.判定关键字是否在Map中的方法
这里有三种判断关键字是否在Map中的方法。
① 用count()函数:
由于Map的特性,一对一的映射关系(且无重复元素),决定了count函数的返回值只有两个,要么是0,要么是1。若关键字在Map中,count函数返回1;不在则返回0。
使用形式:count(Key)
② 用find()函数:
用find函数来定位数据出现位置,它返回的一个迭代器。当数据出现时,它返回数据所在位置的迭代器,如果map中没有要查找的数据,它返回的迭代器等于end函数返回的迭代器。
map<char,int>Map;
map<char,int>::iterator iter=Map.find(Key);
if(iter==Map.end())cout<<"can't find"<<endl;
else cout<<"find"<<endl;
③ 用lower_bound()函数和upper_bound()函数或者equal_value()函数:(虽然方法显示笨重了些,但是体现了Map数据的有序性)
ower_bound():返回键值给定元素的第一个位置(下界)
upper_bound():返回键值给定元素的第一个位置(上界)
equal_range():返回特殊条目的迭代器对。具体表现为:
返回一个pair,pair里面第一个变量是lower_bound返回的迭代器,pair里面第二个迭代器是upper_bound返回的迭代器。
如果这两个迭代器(上、下界)相等的话,则说明Map中不存在这个关键字。
if(iter_1!=iter_2)cout<<"find"<<endl;
else cout<<"can't find"<<endl;
完整代码,测试lower_bound、upper_bound、equal_value函数 :
#include<iostream>
#include<cstdio>
#include<map>
using namespace std;
typedef map<char,int>m;
m Map;
int main()
{
char ch;
for(int i=0;i<5;i++)
{
cout<<"input character:";
cin>>ch;
Map[ch]=i;
}
m::iterator iter_1,iter_2;
iter_1=Map.lower_bound('C');
iter_2=Map.upper_bound('C');
cout<<"lower_bound:\n";
cout<<iter_1->first<<"->"<<iter_1->second<<endl;
cout<<"upper_bound:\n";
cout<<iter_2->first<<"->"<<iter_2->second<<endl;
cout<<"*********************\n";
cout<<"queal_value:\n";
pair<m::iterator,m::iterator> p=Map.equal_range('C');
iter_1=p.first;
iter_2=p.second;
cout<<iter_1->first<<"->"<<iter_1->second<<endl;
cout<<iter_2->first<<"->"<<iter_2->second<<endl;
}
查找关键字:
find函数:
返回一个pair对象,pair->first表示Key的值,pair->second表示Key_Value的值。
result = Map.find(Key);
cout << result->first << "->" << result->second << endl;
六.Map的基本操作函数
*begin():返回指向Map头部的迭代器
*clear():删除所有元素
*count():返回指定元素出现的次数,Map中只有0和1两个值
*empty():如果Map为空,则返回true
*end():返回指向Map末尾的迭代器
equal_range():返回特殊条目的迭代器对
*erase():删除一个元素
*find():查找一个元素*
insert():插入元素
*rbegin():返回一个指向Map尾部的反向迭代器
*rend():返回一个指向Map头部的反向迭代器
*size():返回map中元素的个数
*swap():交换两个Map容器
equal_range():返回特殊条目的迭代器对
lower_bound():返回键值>=给定元素的第一个位置
upper_bound():返回键值>给定元素的第一个位置
value_comp():返回比较元素Key_Value的函数
max_size():返回可以容纳的最大元素个数
key_comp():返回比较元素Key的函数
七.对Map进行排序
①自然数值key的排序
Map是有序的,自动按照Key进行升序排序(STL中默认是采用小于号来排序的)。下面我们通过前面的一段代码来验证Map的有序性。
#include<iostream>
#include<cstdio>
#include<map>
using namespace std;
typedef map<char,int>m;
m Map;
int main()
{
char ch;
for(int i=0;i<5;i++)
{
cout<<"input character:";
cin>>ch;
//Map.insert(m::value_type(ch,i));
//Map.insert(pair<char,int>(ch,i));
Map[ch]=i;
}
// 前向迭代器遍历
map<char,int>::iterator iter;// 前向迭代器
for(iter=Map.begin();iter!=Map.end();iter++)
{
cout<<iter->first<<" "<<iter->second<<endl;
}
}
既然是按照小于号来排序的,那么,如果插入的Key的类型是类或者结构体,因为结构体不支持小于号运算,所以当涉及到排序操作时就会出现问题。
如何解决非数值类型的排序比较呢?主要有下面两种方法。
②非数值类型的key排序
重载小于号
#include<iostream>
#include<cstdio>
#include<vector>
#include<map>
using namespace std;
typedef struct Student
{
int age;
int score;
// 重载小于号
bool operator <(Student const& temp)const
{
if(age<temp.age)return true;
else if(age==temp.age)
{
if(score<temp.score)return true;
else return false;
}
else if(age>temp.age)return false;
}
}Student;
vector<Student>students;
typedef map<Student,int> m;
m Map;
int main()
{
int n;
cout<<"input the total numberof students:";
cin>>n;
Student temp;
for(int i=0;i<n;i++)
{
cout<<"input the age and score of student:";
cin>>temp.age>>temp.score;
Map[temp]=i;
students.push_back(temp);
}
m::iterator iter;
for(iter=Map.begin();iter!=Map.end();iter++)
{
cout<<iter->first.age<<" "<<iter->first.score<<endl;
}
}
仿函数的应用
(这个时候结构体中没有直接的小于号重载)
#include<iostream>
#include<cstdio>
#include<vector>
#include<map>
using namespace std;
typedef struct Student
{
int age;
int score;
}Student;
vector<Student>students;
// 仿函数
class sort
{
public:
bool operator()(Student const &A,Student const &B)
{
if(A.age<B.age)return true;
else if(A.age==B.age)
{
if(A.score<B.score)return true;
else return false;
}
else if(A.age>B.age)return false;
}
};
typedef map<Student,int,sort> m;
m Map;
int main()
{
int n;
cout<<"input the total numberof students:";
cin>>n;
Student temp;
for(int i=0;i<n;i++)
{
cout<<"input the age and score of student:";
cin>>temp.age>>temp.score;
Map[temp]=i;
students.push_back(temp);
}
m::iterator iter;
for(iter=Map.begin();iter!=Map.end();iter++)
{
cout<<iter->first.age<<" "<<iter->first.score<<endl;
}
}
八.Map的删除
当使用完了Map容器后,我们可以将 容器清空或者删除处理,以腾出空闲的空间来。接下来看一下Map容器的删除函数。
earse()函数:删除Map中的条目(一个或多个)
该成员方法的定义如下:
iterator erase(iterator it);// 通过一个条目对象删除
iterator erase(iterator first,iterator last)// 删除一个范围
size_type erase(const Key&key);// 通过关键字删除
clear()函数:直接将整个Map删除
相当于erase(Map.begin(),Map.end())
另外有关于Map容器的嵌套使用和嵌套删除,有些固定的使用方法,比如删除嵌套Map容器的元素,需要遍历一个一个erase,具体参见博客;
参考:
https://www.jianshu.com/u/d9e886863e82
https://www.cnblogs.com/pualus/p/9319091.html