map是用来存放<key, value>键值对的数据结构,可以很方便快速的根据key查到相应的value。假如存储学生和其成绩(假定不存在重名,当然可以对重名加以区分),我们用map来进行存储就是个不错的选择。 我们这样定义,map<string, int>,其中学生姓名用string类型,作为Key;该学生的成绩用int类型,作为value。这样一来,我们可以根据学生姓名快速的查找到他的成绩。
但是,我们除了希望能够查询某个学生的成绩,或许还想看看整体的情况。我们想把所有同学和他相应的成绩都输出来,并且按照我们想要的顺序进行输出:比如按照学生姓名的顺序进行输出,或者按照学生成绩的高低进行输出。换句话说,我们希望能够对map进行按Key排序或按Value排序,然后按序输出其键值对的内容。
其实,为了实现快速查找,map内部本身就是按序存储的(比如红黑树)。在我们插入<key, value>键值对时,就会按照key的大小顺序进行存储。这也是作为key的类型必须能够进行<运算比较的原因。现在我们用string类型作为key,因此,我们的存储就是按学生姓名的字典排序储存的。我们都知道在使用map的时候是按照key从小到大排序的,并不是说map只能这样排序只不过从小到大是其默认的排序方式罢了。我们完全可以指定其他的排序方式。
一、C++ STL中Map的按Key从大到小的排序
map是stl里面的一个模板类,现在我们来看下map的定义:
template < class Key, class T, class Compare = less<Key>,
class Allocator = allocator<pair<const Key,T> > > class map;
它有四个参数,其中我们比较熟悉的有两个: Key 和 Value。第四个是 Allocator,用来定义存储分配模型的,此处我们不作介绍。
现在我们重点看下第三个参数: class Compare = less<Key>
这也是一个class类型的,而且提供了默认值 less<Key>。 就是这个默认的less<key>决定了从下到大的排序方式。
less是stl里面的一个函数对象,那么什么是函数对象呢?
所谓的函数对象:即调用操作符的类,其对象常称为函数对象(function object),它们是行为类似函数的对象。表现出一个函数的特征,就是通过“对象名+(参数列表)”的方式使用一个 类,其实质是对operator()操作符的重载。
现在我们来看一下less的实现:
template <class T> struct less : binary_function <T,T,bool> {
bool operator() (const T& x, const T& y) const
{return x<y;}
};
它是一个带模板的struct,里面仅仅对()运算符进行了重载,实现很简单,但用起来很方便,这就是函数对象的优点所在。stl中还为四则运算等常见运算定义了这样的函数对象,与less相对的还有greater:
template <class T> struct greater : binary_function <T,T,bool> {
bool operator() (const T& x, const T& y) const
{return x>y;}
};
map这里指定less作为其默认比较函数(对象),所以我们通常如果不自己指定Compare,map中键值对就会按照Key的less顺序进行组织存储,因此我们就看到了上面代码输出结果是按照学生姓名的字典顺序输出的,即string的less序列。
我们可以在定义map的时候,指定它的第三个参数Compare,比如我们把默认的less指定为greater。如下所示即可更改排序方式为从大到小。map<string,string,greater<string>> mymap;
#include<iostream>
#include <map>
#include <string>
using namespace std;
int main(){
//map<string,string> mymap;
map<string,string,greater<string>> mymap;
mymap["a"]="student1";
mymap["b"]="student2";
mymap["c"]="student3";
mymap["d"]="student4";
for(auto iter=mymap.begin();iter!=mymap.end();++iter){
cout<<(*iter).first<<"->"<<(*iter).second<<endl;
}
return 0;
}
如下所示程序的输出为:
二、C++ STL中指定Map的排序方式
如何实现map按照自定义的方式进行排序呢?例如,我想按照学生的名字的长度进行排序,如何来做?见如下程序实例。
#include<iostream>
#include <map>
#include <string>
using namespace std;
//这里不用吧Compare定义为模板,直接指定他的参数为string就好了。
struct CmpByKeyLength{
bool operator()(const string& k1,const string& k2){
return k1.length()<k2.length();
}
};
int main(){
//map<string,string> mymap;
//map<string,int> mymap;
map<string,int,CmpByKeyLength> mymap;
mymap["b"]=90;
mymap["aa"]=92;
mymap["bbb"]=98;
mymap["aaaa"]=90;
for(auto iter=mymap.begin();iter!=mymap.end();++iter){
cout<<(*iter).first<<"->"<<(*iter).second<<endl;
}
return 0;
}
程序执行结果如下:
三、key是结构体的Map排序
见如下程序实例:
#include<iostream>
#include <map>
#include <string>
using namespace std;
struct stu_info{
int id;
string name;
bool operator<(stu_info const& r) const{
if(id<r.id) return true;
if(id==r.id) return name.compare(r.name)<0;
return false;
}
};
int main(){
stu_info info1;
map<stu_info,int> mymap;
info1.id=2;
info1.name="yangmi";
mymap[info1]=90;
info1.id=2;
info1.name="tongliya";
mymap[info1]=99;
for(auto iter=mymap.begin();iter!=mymap.end();++iter){
// cout<<(*iter).first<<"->"<<(*iter).second<<endl;
cout<<"id="<<iter->first.id;
cout<<",name="<<iter->first.name;
cout<<",score="<<iter->second<<endl;
}
return 0;
}
程序执行结果如下:
四、将map按value排序
实现思路:借助vector。先把map的元素按照pair形式插入到vector中,再对vector进行排序(用一个新写的比较函数),这样就可以实现按照map的value排序了。
知识点一:将map中的pair元素放入vector中去
vector<pair<string,int>> myvec(mymap.begin(),mymap.end());
示例程序如下:
//演示按照value对map进行排序
#include<iostream>
#include <map>
#include <vector>
#include <string>
#include <algorithm>
#include <utility>
/*这个语句会出错,奇怪???*/
//typedef pair<string,int> PAIR;
using namespace std;
//以下两个cmp函数任一均可用于sort
//bool cmp(const PAIR& pair1,const PAIR& pair2){
bool cmp(const pair<string,int>& pair1,const pair<string,int>& pair2){
return pair1.second<pair2.second;
}
int main(){
map<string,int> mymap;
mymap["tongliya"]=99;
mymap["yangmi"]=90;
mymap["dongjie"]=88;
mymap["liuyifei"]=92;
//vector<PAIR> myvec(mymap.begin(),mymap.end());
vector<pair<string,int>> myvec(mymap.begin(),mymap.end());
sort(myvec.begin(),myvec.end(),cmp);
for(auto iter=myvec.begin();iter!=myvec.end();++iter){
cout<<"name="<<iter->first;
cout<<",score="<<iter->second<<endl;
}
return 0;
}
运行结果如下:
注:typedef pair<string,int> PAIR;语句会报错“‘pair’不是一个类型”,有点奇怪。