1、Map
(1)定义
Map是标准关联式容器(associativecontainer)之一,
一个map是一个键值对序列,即(key,value)对。它提供
基于key的快速检索能力,
在一个map中key值是唯一的。map提供双向迭代器,即有从前往后的(iterator),也有从后往前的(reverse_iterator)。
Map要求能对key进行<操作,且保持按key值递增有序,因此map上的迭代器也是递增有序的。
如果对于元素并不需要保持有序,可以使用hash_map。
Map中key值是唯一的,如果已存在一个键值对(name,code):("name_a",1),而我们还想插入一个键值对("name_a",1)则会报错(不是报错,准确的说是,返回插入不成功!)。而我们又的确想这样做,即
一个键对应多个值,multimap可实现这个功能。
(2)底层实现
Map内部自建一颗红黑树(一种非严格意义上的平衡二叉树),这颗树具有对数据自动排序的功能,所以在map内部所有的数据都是有序的,后边我们会见识到有序的好处。
由于STL是一个统一的整体,map的很多用法都和STL中其它的东西结合在一起,比如在排序上,默认用的是小于号,即less<>。
Map中由于它内部有序,由红黑树保证,因此很多函数执行的时间复杂度都是log2N的,如果用map函数可以实现的功能,而STLAlgorithm也可以完成该功能,建议用map自带函数,效率高一些。
Map在空间上的特性,由于map的每个数据对应红黑树上的一个节点,这个节点在不保存你的数据时,是占用16个字节的,一个父节点指针,左右孩子指针,还有一个枚举值(标示红黑的,相当于平衡二叉树中的平衡因子),这些地方很费内存。
(3)特点
Map类似于数据库中的1:1关系,它是一种关联容器,提供一对一(C++primer中文版中将第一个译为键,每个键只能在map中出现一次,第二个被译为该键对应的值)的数据处理能力,这种特性了使得map类似于数据结构里的红黑二叉树。Multimap类似于数据库中的1:N关系,它是一种关联容器,提供一对多的数据处理能力。
(4)性能分析
a)、hash_map和map的区别在哪里?(1)构造函数hash_map需要hash函数,等于函数;map只需要比较函数(小于函数)。
(2)存储结构hash_map采用hash表存储,map一般采用红黑树实现。因此内存数据结构是不一样的。
b)、什么时候需要使用hash_map,什么时候需要map?总体来说,hash_map查找速度会比map快,而且查找速度基本和数据数据量大小,属于常数级别;而map的查找速度是log(n)级别。
但若你对内存使用特别严格,希望程序尽可能少消耗内存,那么一定要小心,hash_map可能会让你陷入尴尬,特别是当你的hash_map对象特别多时,你就更无法控制了,而且hash_map的构造速度较慢。
因此,选择的时候需要权衡三个因素:查找速度,数据量,内存使用。
c)、如何用hash_map替换程序中已有的map容器?这个很容易,但需要你有良好的编程风格。建议你尽量使用typedef来定义你的类型:
typedefmapKeyMap;
当你希望使用hash_map来替换的时候,只需要修改:
typedefhash_mapKeyMap;
其他的基本不变。当然,你需要注意是否有Key类型的hash函数和比较函数。
2、成员函数
(1)构造函数
map();//默认构造函数
map(constmap&m)//拷贝构造函数
map(iteratorbegin,iteratorend);//区间构造函数
map(iteratorbegin,iteratorend,consttraits&_compare)//带比较谓词的构造函数
map(iteratorbegin,iteratorend,consttraits&_compare,constallocator&all)//带分配器
经过分析发现,map的构造函数主要是调用“拷贝构造函数”和利用“迭代器”进行初始化两种方式,因为map中每个节点由一对值构成。
(2)插入操作
(1)、insert(pair<T1,T2,>(key1,value1))
//用insert函数插入pair数据
(2)、insert(map<T1,T2>::value_type(key1,value1));
//用insert函数插入value_type数据,这种插入方式和第一种基本相似
(3)、利用数组进行插入
示例:
#include<map>
#include<string>
#include<iostream>
Usingnamespacestd;
Intmain()
{
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”));
map<int,string>::iteratoriter;
for(iter=mapStudent.begin();iter!=mapStudent.end();iter++)
{
Cout<<iter->first<<””<<iter->second<<end;
}
}
#include<map>
#include<string>
#include<iostream>
Usingnamespacestd;
Intmain()
{
Map<int,string>mapStudent;
mapStudent.insert(map<int,string>::value_type(1,“student_one”));
mapStudent.insert(map<int,string>::value_type(2,“student_two”));
mapStudent.insert(map<int,string>::value_type(3,“student_three”));
map<int,string>::iteratoriter;
for(iter=mapStudent.begin();iter!=mapStudent.end();iter++)
{
Cout<<iter->first<<””<<iter->second<<end;
}
}
#include<map>
#include<string>
#include<iostream>
Usingnamespacestd;
Intmain()
{
Map<int,string>mapStudent;
mapStudent[1]=“student_one”;
mapStudent[2]=“student_two”;
mapStudent[3]=“student_three”;
map<int,string>::iteratoriter;
for(iter=mapStudent.begin();iter!=mapStudent.end();iter++)
{
Cout<<iter->first<<””<<iter->second<<end;
}
}
以上三种用法,虽然都可以实现数据的插入,但是它们是有区别的:第一种和第二种在效果上是一样的,用insert函数插入数据,在数据的插入上涉及到集合的唯一性这个概念,即当map中有这个关键字时,insert操作是插入数据不了的,但是用数组方式就不同了,它可以覆盖以前该关键字对应的值,如下:
mapStudent.insert(map<int,string>::value_type(1,“student_one”));
mapStudent.insert(map<int,string>::value_type(1,“student_two”));
上面这两条语句执行后,map中1这个关键字对应的值是“student_one”,第二条语句并没有生效,那么我们怎么知道insert语句是否插入成功,可以用pair来获得是否插入成功,程序如下:
Pair<map<int,string>::iterator,bool>Insert_Pair;
Insert_Pair=mapStudent.insert(map<int,string>::value_type(1,“student_one”));
我们通过pair的第二个变量来知道是否插入成功,它的第一个变量返回的是一个map的迭代器,如果插入成功的话:Insert_Pair.second应该是true的,否则为false。
下面给出完成代码,演示插入成功与否问题:
#include<map>
#include<string>
#include<iostream>
Usingnamespacestd;
Intmain()
{
Map<int,string>mapStudent;
Pair<map<int,string>::iterator,bool>Insert_Pair;
Insert_Pair=mapStudent.insert(pair<int,string>(1,“student_one”));
If(Insert_Pair.second==true)
{
Cout<<”InsertSuccessfully”<<endl;
}
Else
{
Cout<<”InsertFailure”<<endl;
}
Insert_Pair=mapStudent.insert(pair<int,string>(1,“student_two”));
If(Insert_Pair.second==true)
{
Cout<<”InsertSuccessfully”<<endl;
}
Else
{
Cout<<”InsertFailure”<<endl;
}
map<int,string>::iteratoriter;
for(iter=mapStudent.begin();iter!=mapStudent.end();iter++)
{
Cout<<iter->first<<””<<iter->second<<end;
}
}
用如下程序,看下用数组插入在数据覆盖上的效果:
#include<map>
#include<string>
#include<iostream>
Usingnamespacestd;
Intmain()
{
Map<int,string>mapStudent;
mapStudent[1]=“student_one”;
mapStudent[1]=“student_two”;
mapStudent[2]=“student_three”;
map<int,string>::iteratoriter;
for(iter=mapStudent.begin();iter!=mapStudent.end();iter++)
{
Cout<<iter->first<<””<<iter->second<<end;
}
}
(3)删除操作
(1)、erase(map<T1,T2>::iteratoriter);
//删除迭代器所指的节点
(2)、erase(keyk);
//根据键值进行删除,删除键值k所指的节点
(3)、erase(map<T1,T2>::iteratormapiter1,<T1,T2>::iteratoriter2);
//删除iter1和iter2之间的数据。
clear();
//清空map中的数据可以用clear()函数,判定map中是否有数据可以用empty()函数,它返回true则说明是空map
示例:
#pragmawarning(disable:4786)
#include<iostream>
#include<string>
#include<map>
usingnamespacestd;
intmain()
{
/*
map<int,string>tmp;
map<int,string>::const_iteratoriter1,iter2;
tmp.insert(pair<int,string>(54090104,"Bob"));
tmp.insert(pair<int,string>(54090105,"Ben"));
iter1=tmp.begin();
iter2=tmp.end();
*/
map<int,string>studentMessage;
map<int,string>::iteratoriter;
//向map中插入数据
studentMessage.insert(pair<int,string>(54090101,"Mike"));
studentMessage.insert(pair<int,string>(54090101,"MIKE"));//重复插入
studentMessage.insert(map<int,string>::value_type(54090102,"Sam"));
studentMessage.insert(map<int,string>::value_type(54090102,"SAM"));//重复插入
studentMessage[54090103]="Jake";
studentMessage[54090103]="JAKE";//重复插入
//为了测试删除,先插入两个数据,看插入结果主要看上面的插入方式
studentMessage[54090104]="Bob";
studentMessage[54090105]="Ben";
cout<<"完成插入后map中的数据:"<<endl;
for(iter=studentMessage.begin();iter!=studentMessage.end();++iter)
{
cout<<iter->first<<""<<iter->second<<endl;
}
//从map中删除数据
iter=studentMessage.begin();
studentMessage.erase(iter);
cout<<"利用迭代器删除map中第一个元素:"<<endl;
for(iter=studentMessage.begin();iter!=studentMessage.end();++iter)
{
cout<<iter->first<<""<<iter->second<<endl;
}
studentMessage.erase(54090102);
cout<<"利用键值删除map中的第一个元素:"<<endl;
for(iter=studentMessage.begin();iter!=studentMessage.end();++iter)
{
cout<<iter->first<<""<<iter->second<<endl;
}
studentMessage.erase(studentMessage.begin(),studentMessage.end());
cout<<"利用范围迭代器删除map中的所有数据:"<<endl;
for(iter=studentMessage.begin();iter!=studentMessage.end();++iter)
{
cout<<iter->first<<""<<iter->second<<endl;
}
return0;
}
(4)定位查找
begin();
//返回指向map头部的迭代器
end();
//返回指向map末尾的迭代器
(1)、count();
//用count函数来判定关键字是否出现,其缺点是无法定位数据出现位置,由于map的特性,一对一的映射关系,就决定了count函数的返回值只有两个,要么是0,要么是1,出现的情况,当然是返回1了
(2)、find();
//用find函数来定位数据出现位置,它返回的一个迭代器,当数据出现时,它返回数据所在位置的迭代器,如果map中没有要查找的数据,它返回的迭代器等于end函数返回的迭代器
(3)、Lower_bound();Upper_bound();
//这个方法用来判定数据是否出现,是显得笨了点:
//Lower_bound函数用法,这个函数用来返回要查找关键字的下界(是一个迭代器)
//Upper_bound函数用法,这个函数用来返回要查找关键字的上界(是一个迭代器)
//例如:map中已经插入了1,2,3,4的话,如果lower_bound(2)的话,返回的2,而upper-bound(2)的话,返回的就是3
Equal_range();
//函数返回一个pair,pair里面第一个变量是Lower_bound返回的迭代器,pair里面第二个迭代器是Upper_bound返回的迭代器,如果这两个迭代器相等的话,则说明map中不出现这个关键字
示例:
#include<map>
#include<string>
#include<iostream>
Usingnamespacestd;
Intmain()
{
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”));
map<int,string>::iteratoriter;
iter=mapStudent.find(1);
if(iter!=mapStudent.end())
{
Cout<<”Find,thevalueis”<<iter->second<<endl;
}
Else
{
Cout<<”DonotFind”<<endl;
}
}
#include<map>
#include<string>
#include<iostream>
Usingnamespacestd;
Intmain()
{
Map<int,string>mapStudent;
mapStudent[1]=“student_one”;
mapStudent[3]=“student_three”;
mapStudent[5]=“student_five”;
map<int,string>::iteratoriter;
iter=mapStudent.lower_bound(2);
{
//返回的是下界3的迭代器
Cout<<iter->second<<endl;
}
iter=mapStudent.lower_bound(3);
{
//返回的是下界3的迭代器
Cout<<iter->second<<endl;
}
iter=mapStudent.upper_bound(2);
{
//返回的是上界3的迭代器
Cout<<iter->second<<endl;
}
iter=mapStudent.upper_bound(3);
{
//返回的是上界5的迭代器
Cout<<iter->second<<endl;
}
Pair<map<int,string>::iterator,map<int,string>::iterator>mapPair;
mapPair=mapStudent.equal_range(2);
if(mapPair.first==mapPair.second)
{
cout<<”DonotFind”<<endl;
}
Else
{
Cout<<”Find”<<endl;
}
mapPair=mapStudent.equal_range(3);
if(mapPair.first==mapPair.second)
{
cout<<”DonotFind”<<endl;
}
Else
{
Cout<<”Find”<<endl;
}
}
(5)数据大小
max_size();
//返回map容器可能包含的元素最大个数
size();
//返回当前map容器中的元素个数
count();
//用来查找map中某个某个键值出现的次数;
示例:
#pragmawarning(disable:4786)
#include<map>
#include<string>
#include<iostream>
usingnamespacestd;
intmain()
{
map<int,string>studentMessage;
map<int,string>::iteratoriter;
studentMessage.insert(pair<int,string>(54090101,"Mike"));
studentMessage.insert(pair<int,string>(54090102,"Sam"));
studentMessage.insert(pair<int,string>(54090103,"Jake"));
//begin获取map中的第一个元素的迭代器,并且等于rend
//end获取map中的最后一个元素下一位置的迭代器,并且等于rbegin
cout<<"迭代器中的元素如下:"<<endl;
for(iter=studentMessage.begin();iter!=studentMessage.end();++iter)
{
cout<<iter->first<<""<<iter->second<<endl;
}
//看看max_size和size的值得意义
cout<<"map的max_size的值:"<<studentMessage.max_size()<<endl;
cout<<"map的size的值:"<<studentMessage.size()<<endl;
//看看empty和clear的使用
studentMessage.clear();
if(studentMessage.empty())
{
cout<<"ThemapisEmpty!!"<<endl;
}
else
{
cout<<"ThemapisnotEmpty!!"<<endl;
}
return0;
}
(6)遍历操作
第一种:应用前向迭代器
第二种:应用反相迭代器
第三种:用数组方式
示例:
#include<map>
#include<string>
#include<iostream>
Usingnamespacestd;
Intmain()
{
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”));
map<int,string>::reverse_iteratoriter;
for(iter=mapStudent.rbegin();iter!=mapStudent.rend();iter++)
{
Cout<<iter->first<<””<<iter->second<<end;
}
}
#include<map>
#include<string>
#include<iostream>
Usingnamespacestd;
Intmain()
{
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”));
intnSize=mapStudent.size()
//此处有误,应该是for(intnIndex=1;nIndex<=nSize;nIndex++)
//byrainfish
for(intnIndex=0;nIndex<nSize;nIndex++)
{
Cout<<mapStudent[nIndex]<<end;
}
}
(7)排序操作
第一种:小于号重载
第二种:仿函数的应用,这个时候结构体中没有直接的小于号重载
示例:
#include<map>
#include<string>
Usingnamespacestd;
TypedefstructtagStudentInfo
{
IntnID;
StringstrName;
}StudentInfo,*PStudentInfo;//学生信息
Intmain()
{
intnSize;
//用学生信息映射分数
map<StudentInfo,int>mapStudent;
map<StudentInfo,int>::iteratoriter;
StudentInfostudentInfo;
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<<endl<<iter->first.strName<<endl<<iter->second<<endl;
}
以上程序是无法编译通过的,只要重载小于号,就OK了,如下:
TypedefstructtagStudentInfo
{
IntnID;
StringstrName;
Booloperator<(tagStudentInfoconst&_A)const
{
//这个函数指定排序策略,按nID排序,如果nID相等的话,按strName排序
If(nID<_A.nID)returntrue;
If(nID==_A.nID)returnstrName.compare(_A.strName)<0;
Returnfalse;
}
}StudentInfo,*PStudentInfo;//学生信息
#include<map>
#include<string>
Usingnamespacestd;
TypedefstructtagStudentInfo
{
IntnID;
StringstrName;
}StudentInfo,*PStudentInfo;//学生信息
Classssort
{
Public:
Booloperator()(StudentInfoconst&_A,StudentInfoconst&_B)const
{
If(_A.nID<_B.nID)returntrue;
If(_A.nID==_B.nID)return_A.strName.compare(_B.strName)<0;
Returnfalse;
}
};
Intmain()
{
//用学生信息映射分数
Map<StudentInfo,int,sort>mapStudent;
StudentInfostudentInfo;
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));
}