C++中的STL中map用法简介

C++中的STL中map用法详解(详细内容参考)

Map是STL的一个关联容器,定义方式:map <key,value> mp;

相当于下标更灵活的数组,其中key为相当于下标,是可以比较大小的类型(不必像普通数组一样下标连续),value可以是任何类型,包含自定义类型】

它提供一对一(其中第一个可以称为关键字,每个关键字只能在map中出现一次,第二个可能称为该关键字的值)的数据 处理能力,

由于这个特性,它完成有可能在我们处理一对一数据的时候,在编程上提供快速通道。

这里说下map内部数据的组织,map内部自建一颗红黑树(一 种非严格意义上的平衡二叉树)

这颗树具有对数据自动排序的功能,查询效率为log(n),所以在map内部所有的数据都是有序的,后边我们会见识到有序的好处。

1、map简介

map是一类关联式容器。它的特点是增加和删除节点对迭代器的影响很小,除了那个操作节点,对其他的节点都没有什么影响。

对于迭代器来说,可以修改实值(即value的值),而不能修改key,一个key只能对应一个值,要有重复元素,得用“multimap”

2、map的功能

自动建立Key-value键值的对应。key 和 value可以是任意你需要的类型。【key 的类型支持比较大小,要重载“<”运算符

根据key值快速查找记录,查找的复杂度基本是Log(N),如果有1000个记录,最多查找10次,1,000,000个记录,最多查找20次。

常用来:快速插入Key -Value 记录、快速删除记录、根据Key 修改value记录、遍历所有记录。

3、使用map

使用map得包含map类所在的头文件

#include <map>  //注意,STL头文件没有扩展名.h

map对象是模板类,需要关键字和存储对象两个模板参数:

std:map<int,string> personnel;

这样就定义了一个用int作为索引关键字,并拥有相关联的指向string值的指针.

为了使用方便,可以对模板类进行一下类型定义,

typedef map<int,CString> UDT_MAP_INT_CSTRING;  //用户自定义Map-int——string 类型;

UDT_MAP_INT_CSTRING enumMap;

4、map的构造函数

map共提供了6个构造函数,这块涉及到内存分配器这些东西,略过不表,在下面我们将接触到一些map的构造方法,这里要说下的就是,我们通常用如下方法构造一个map

map<int, string> mapStudent;

5、数据的插入

在构造map容器后,我们就可以往里面插入数据了。常用数组方式插入数据,下面举例说明【关键值相同时,后插入的值会覆盖前面的值,为了防止覆盖,插入之前先检验,方法参见第6条】

#include <map>  
#include <string>  
#include <iostream>  
using namespace std;  
int main()  
{  
    map<int, string> mapStudent;  
    mapStudent[1] = "student_one";  
    mapStudent[2] = "student_two";  
    mapStudent[3] = "student_three";        
    for(auto iter = mapStudent.begin(); iter != mapStudent.end(); iter++)  
        cout<<iter->first<<' '<<iter->second<<endl;  
}  

6、查找并获取map中的元素(包括判定这个关键字是否在map中出现)

在这里我们将体会,map在数据插入时保证有序的好处。

要判定一个数据(关键字)是否在map中出现的方法比较多,这里标题虽然是数据的查找,在这里将穿插着大量的map基本用法。

这里给出两种数据查找方法

第一种:用count函数来判定关键字是否出现,(不需要定义迭代器更常用),其缺点是无法定位数据出现位置,由于map的特性,一对一的映射关系,就决定了count函数的返回值只有两个,要么是0,要么是1,出现的情况,当然是返回1了

程序说明:

#include <map>    
#include <string>   
#include <iostream>    
using namespace std;  
  
int main()    
{   
    map<int, string> mapStudent;    
    mapStudent[1] = "student_one"; 
    mapStudent[2] = "student_two";   
    mapStudent[3] = "student_three";  
  	for(int i=0;i<10;i++) 
  	{
  	    bool flag=mapStudent.count(i);
	    if(flag) 
	       cout<<"Find, the value is "<<mapStudent[i]<<endl; 
	    else  
	       cout<<i<<"'s value Do not Find"<<endl;
	}     
    return 0;  
}

结果是:

0's value Do not Find
Find, the value is student_one
Find, the value is student_two
Find, the value is student_three
4's value Do not Find
5's value Do not Find
6's value Do not Find
7's value Do not Find
8's value Do not Find
9's value Do not Find

第二种:用find函数来定位数据出现位置,它返回的一个迭代器,当数据出现时,它返回数据所在位置的迭代器,如果map中没有要查找的数据,它返回的迭代器等于end()函数返回的迭代器。

查找map中是否包含某个关键字条目用find()方法,传入的参数是要查找的key,在这里需要提到的是begin()和end()两个成员,

在map对象中:begin( )代表第一个条目 ;

                         end() 最后一个条目后为空的那个条目,这两个数据的类型是iterator迭代器.

程序说明:

#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"));  
      
    auto iter = mapStudent.find(1);  
    if(iter != mapStudent.end())  
       cout<<"Find, the value is "<<iter->second<<endl;  
    else  
       cout<<"Do not Find"<<endl;  
    return 0;  
}  

通过map对象的方法获取的iterator数据类型是一个std::pair对象,包括两个数据 iterator->first和 iterator->second分别代表关键字和存储的数据。

7、map的大小

在往map里面插入了数据,我们怎么知道当前已经插入了多少数据呢,可以用size函数,用法如下:

Int nSize = mapStudent.size();

8、数据的遍历

一、数组的形式,程序说明如下:【方便局限性大,下标必须连续

#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;  
}  

二、迭代器的形式,程序说明如下:【更通用,请用auto 定义迭代器

#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"));     
  
    for(auto iter = mapStudent.begin(); iter != mapStudent.end(); iter++)  
        cout<<iter->first<<' '<<iter->second<<endl; 
}  

9、从map中删除元素

移除某个map中某个条目用erase()

该成员方法的定义如下:

iterator erase(iterator it);//通过一个条目对象删除
iterator erase(iterator first,iterator last)//删除一个范围
size_type erase(const Key&key);//通过关键字删除

clear()就相当于

enumMap.erase(enumMap.begin(),enumMap.end());

这里要用到erase函数,它有三个重载了的函数,下面在例子中详细说明它们的用法:

#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[3]= "student_three";    
       //如果你要演示输出效果,请选择以下的一种,你看到的效果会比较好    
       //如果要删除1,用迭代器删除    
       auto iter =mapStudent.find(1);    
       mapStudent.erase(iter);  
	   cout<<mapStudent.size()<<endl;  
       //如果要删除1,用关键字删除    
       int n = mapStudent.erase(1);//如果删除了会返回1,否则返回0   
	   cout<<n<<endl; 
	   cout<<mapStudent.size()<<endl;
       //用迭代器,成片的删除    
       //一下代码把整个map清空    
       mapStudent.erase( mapStudent.begin(), mapStudent.end() );   
       //成片删除要注意的是,也是STL的特性,删除区间是一个前闭后开的集合    
       //自个加上遍历代码,打印输出吧    
       cout<<mapStudent.size()<<endl;
}

10、map中的swap用法

map中的swap不是一个容器中的元素交换,而是两个容器所有元素的交换。

11、排序-map中的sort问题

map中的元素是自动按Key升序排序,所以不能对map用sort函数;

这里要讲的是一点比较高深的用法了,排序问题,STL中默认是采用小于号来排序的,以上代码在排序上是不存在任何问题的,因为上面的关键字是int 型,它本身支持小于号运算,在一些特殊情况,比如关键字是一个结构体,涉及到排序就会出现问题,因为它没有小于号操作,insert等函数在编译的时候过 不去,下面给出两个方法解决这个问题:

第一种:小于号重载,程序举例:

#include <iostream>  
#include <string>  
#include <map>  
using namespace std;  
  
typedef struct tagStudentinfo  
{  
       int      niD; 
       string   strName;  
       bool operator < (tagStudentinfo const& _A) const  
       {     //这个函数指定排序策略,按niD排序,如果niD相等的话,按strName排序  
            if(niD < _A.niD) return true;  
            if(niD == _A.niD)  
                return strName.compare(_A.strName) < 0; 
            return false;  
       }  
}Studentinfo, *PStudentinfo; //学生信息  
  
int main()  
{  
    int nSize;   //用学生信息映射分数  
    map<Studentinfo, int>mapStudent;  
    map<Studentinfo, int>::iterator iter;  
    Studentinfo studentinfo;  
    studentinfo.niD = 1;  
    studentinfo.strName = "student_one";  
    mapStudent.insert(pair<Studentinfo, int>(studentinfo, 90));  
    studentinfo.niD = 2;  
    studentinfo.strName = "student_two";  
    mapStudent.insert(pair<Studentinfo, int>(studentinfo, 80)); 
    for (iter=mapStudent.begin(); iter!=mapStudent.end(); iter++) 
        cout<<iter->first.niD<<' '<<iter->first.strName<<' '<<iter->second<<endl;  
    return 0;  
}  

第二种:仿函数的应用,这个时候结构体中没有直接的小于号重载,程序说明:

//第二种:仿函数的应用,这个时候结构体中没有直接的小于号重载,程序说明  
  
#include <iostream>  
#include <map>  
#include <string>  
using namespace std;  
typedef struct tagStudentinfo  
{  
       int      niD;  
       string   strName;  
}Studentinfo, *PStudentinfo; //学生信息  
  
class sort  
{  
public:  
    bool operator() (Studentinfo const &_A, Studentinfo const &_B) const  
    {  
        if(_A.niD < _B.niD)  
            return true;  
        if(_A.niD == _B.niD)  
            return _A.strName.compare(_B.strName) < 0;  
        return false;  
    }  
};  
  
int main()  
{   //用学生信息映射分数  
    map<Studentinfo, int, sort>mapStudent;  
    map<Studentinfo, int>::iterator iter;  
    Studentinfo studentinfo;  
    studentinfo.niD = 1;  
    studentinfo.strName = "student_one";  
    mapStudent.insert(pair<Studentinfo, int>(studentinfo, 90));  
    studentinfo.niD = 2;  
    studentinfo.strName = "student_two";  
    mapStudent.insert(pair<Studentinfo, int>(studentinfo, 80));  
    for (iter=mapStudent.begin(); iter!=mapStudent.end(); iter++) 
        cout<<iter->first.niD<<' '<<iter->first.strName<<' '<<iter->second<<endl;  
}  

由于STL是一个统一的整体,map的很多用法都和STL中其它的东西结合在一起,比如在排序上,这里默认用的是小于号,即less<>,如果要从大到小排序呢,这里涉及到的东西很多,在此无法一一加以说明。

还要说明的是,map中由于它内部有序,由红黑树保证,因此很多函数执行的时间复杂度都是log2N的,如果用map函数可以实现的功能,而STL Algorithm也可以完成该功能,建议用map自带函数,效率高一些。

下面说下,map在空间上的特性,否则,估计你用起来会有时候表现的比较郁闷,由于map的每个数据对应红黑树上的一个节点,这个节点在不保存你的 数据时,是占用16个字节的,一个父节点指针,左右孩子指针,还有一个枚举值(标示红黑的,相当于平衡二叉树中的平衡因子),我想大家应该知道,这些地方 很费内存了吧,不说了……

12、map的基本操作函数:

C++ maps是一种关联式容器,包含“关键字/值”对

begin()         返回指向map头部的迭代器
clear()        删除所有元素
count()         返回指定元素出现的次数
empty()         如果map为空则返回true
end()           返回指向map末尾的迭代器
equal_range()   返回特殊条目的迭代器对
erase()         删除一个元素
find()          查找一个元素
get_allocator() 返回map的配置器
insert()        插入元素
key_comp()      返回比较元素key的函数
lower_bound()   返回键值>=给定元素的第一个位置
max_size()      返回可以容纳的最大元素个数
rbegin()        返回一个指向map尾部的逆向迭代器
rend()          返回一个指向map头部的逆向迭代器
size()          返回map中元素的个数
swap()           交换两个map
upper_bound()    返回键值>给定元素的第一个位置
value_comp()     返回比较元素value的函数
posted on 2021-10-08 15:26  幽遇  阅读(241)  评论(0编辑  收藏  举报