C++:关联容器(pair、map、set、multiset)

关联容器和顺序容器的本质区别:
关联容器是通过键存取和读取元素、顺序容器通过元素在容器中的位置顺序存储和访问元素。
两个基本的关联容器类型是map和set。
map的元素以键-值对的形式组织:键用作元素在map的索引,而值则表示所存储和读取的数据。set仅包含一个键,并有效地支持关于某个键是否存在的查询。
set和map类型的对象不允许为同一个键添加第二个元素。如果一个键必须对应多个实例,则需使用multimap或mutiset类型,这两种类型允许多个元素拥有相同的键。

1. pair类型

pair类型的初始化

在头文件utility中:
pair<T1,T2> p1;
创建一个pair对象,两个元素的类型分别是T1,T2类型,采用初值进行初始化
pair<T1,T2> p1(v1,v2);
创建一个pair对象,两个元素的类型分别是T1,T2类型,采用v1,v2分别进行初始化
make_pair(v1,v2);
以v1、v2的值创建一个pair对象,类型分别是v1、v2的类型
p.first
返回p中第一个公有数据成员
p.second
返回p中第二个公有数据成员

2. map关联数组

2.1 map初始化

map<k,v> m1
创建一个名为m1的空的map对象,其键和值的类型分别为k、v
map<k, v> m(m2)
创建m2的副本m,m与m2的类型必须有相同的键类型和值类型
map<k,v> m(b,e)
创建map类型的对象m,存储迭代器b和e标记范围内所有元素的副本,元素的类型必须转换为pair

2.2 map定义的类型

map<K,V>::key_type:在map中键的类型
map<K,V>::mapped_type :在map中键所关联的值的类型
map<K,V>::value_type:一个pair类型,它的first元素具有const map<K,V>::key::type 类型,而second元素则为map<K,V>::mapped_type类型

map迭代器进行解引用将产生pair类型的对象,first是键,second是值,:

map<string,int>::iterator map_it = ord_count.begin();
cout<<map_it->first<<""<<map_it->second<<endl;

2.3 给map添加成员

使用下标访问map对象:在map中查找键值为x的元素。如果找到则返回它的值(类型是map<k,v>::mapped_type),否则插入一个新的对象,键值为x,值为map<k,v>中v的默认初值。
因此,使用下标访问map与使用下标访问数组或vector的行为不同,用下标访问不存在的元素将导致在map容器中添加一个新的元素,它的键即为该下标值。这一特性可以使程序简练:

int main()  
{  
    string str;  
    map<string,int> wordCount;  
    while(cin>>str)  
    {  
        ++wordCount[str];  
    }  

    map<string,int>::iterator it_map = wordCount.begin();  
    cout<<"word"<<"\t\t"<<"count"<<endl;  
    for(;it_map != wordCount.end();++it_map)  
        cout<<it_map->first<<"\t\t"<<it_map->second<<endl;  
    return 0;   
}  

2.4 使用insert对map进行插入

m .insert(e)
e是一个用在m上的value_type类型的值,如果e.first不在m中则插入一个值为.second的新元素,否则,即该键值在m中已经存在则保持不变,该函数返回一个pair新类型,包含一个指向键值为e.first的元素的map迭代器,以及一个bool类型的对象,表示是否插入该元素
m.insert(beg,end)
插入beg、end标记范围内的元素,如果该元素的m.first已经存在则不插入,否则插入。返回void类型。
m.insert(iter,e)
如果e.first不在m中,则创建新元素,并以迭代器iter为起点搜索新元素的存储位置,否则返回一个迭代器,指向m中具有给定键的元素。

  //用insert方法重写单词统计程序
  map<string,int> word_count;

 word_count.insert(map<string,int>::value_type("aaa",1));
  map<string,int> word_count;
  string word;
  while(cin>>word)
 {
     pair<map<string,int>::iterator,bool> ret=word_count.insert(make_pair<string,int>(word,1));
    if(!ret.second)//如果没插入成功,证明原来已经存在键值,将统计值+1
  {
       ++ret.first->second;// first是一个迭代器,指向插入的键
   }
  }

2.5 查找和读取map中的元素:

m.count(k)
返回k在m中出现的次数,在map中只是返回0、1(即存在或不存在)
m.find(k)
如果k在m中的键值存在,则返回相应的迭代器,否则返回超出末端的迭代器

   //读取元素而又不插入新元素
    int occurs;
    map<string,int>::iterator it= word_count.find("foobar");//不存在,则返回end迭代器
   if(it!=word_count.end())//可能找不到
   {
    occurs=(*it).second;
    //或者 occurs= it->second
    //通过迭代器访问数据和通过指针很类似
   }

2.6 从map中删除元素

使用erase,与顺序容器功能一样:
m.erase(k)
删除m中键为k的元素。返回值为被删除元素的个数,对于map容器而言,其值必然是0或1。
m.erase(p)
从m中删除迭代器p所指向的元素。返回值为void类型。
m.erase(b,e)
从m中删除一段由一对迭代器限定范围的元素。返回值为void类型。

 //map对象的迭代遍历:

       map<string,int> word_count;

      map<string,int>::const_iterator iter = word_count.begin();
      while(iter!=word_count.end())
       {
         cout<<iter->second<<endl;
         iter++;
      }

2.7 map操作函数总结

//初始化
map<k,v>   m1;//创建一个名为m1的空的map对象,其键和值的类型分别为k、v
map<k, v>  m(m2);//创建m2的副本m,m与m2的类型必须有相同的键类型和值类型
map<k,v>   m(b,e);//创建map类型的对象m,存储迭代器b和e标记范围内所有元素的副本,元素的类型必须转换为pair<const k, v>


//添加元素
m .insert(make_pair(a,b));//如果a不在m中则插入一个值为b的新元素,否则保持不变。
m[a]=b;//该方式下,如果a键不存在,则创建一个<a,b>,否则用b覆盖a键的值


//count或find查找元素
m.count(k);//返回k在m中出现的次数,在map中只是返回0、1(即存在或不存在)
m.find(k);//如果k在m中的键值存在,则返回相应的迭代器,否则返回m.end()


//erase删除元素
m.erase(k);//删除m中键为k的元素。返回值为被删除元素的个数,对于map容器而言,其值必然是0或1。
m.erase(iter);//删除m中迭代器iter指向的元素

//清空map
m.clear();

//map的大小
int mSize=m.size();

//map的遍历
//正向迭代器方式
map<int, string>::iterator  iter;
for(iter = m.begin(); iter != m.end(); iter++)
{
       cout<<iter->first<<'  '<<iter->second<<end;
}
//反向迭代器方式
map<int, string>::reverse_iterator  iter;
for(iter = m.rbegin(); iter != m.rend(); iter++)
{
       cout<<iter->first<<'  '<<iter->second<<end;
}

3. set类型

set只有键,没有值,所以它的vaule_type不是pair类型,而是key_type类型。set也支持以上map的一系列操作,如insert、find、count、erase等。

//初始化
set<int> s0 ;//创建一个名为s0的空的set对象
set<int, greater<int>> s1 ;//创建一个带大于比较器的set, 默认是小于比较器less<int>
int a[3] = {1, 2, 3} ;
set<int> s2(a, a + 3) ;//用数组初始化一个set
set<int> s2(s1) ;//用拷贝构造函数初始化set,s2是s1的副本
set<int> s2(s1.begin(), s1.end()) ;//创建set类型的对象s2,并用两个迭代器所决定区间内的元素初始化


//添加元素
s.insert(a) ;//如果a不在s中则添加a,否则保持不变。
s.insert(s1.begin(), s1.end()) ;//使用迭代器,批量添加元素


//count或find查找元素
s.count(k);//如果s中存在k,则返回1,否则返回0
s.find(k);//如果k在s中存在,则返回相应的迭代器,否则返回s.end()


//erase删除元素
s.erase(2);//删除键为2的元素
s.erase(iter);//删除迭代器iter指定的元素
s.erase(s.begin(), s.end()) ;//删除s内的所有元素

//清空set
s.clear();

//set的大小
int sSize=s.size();


//遍历
//正向迭代器方式
set<int>::iterator  iter;
for(iter = s.begin(); iter != s.end(); iter++)
{
       cout<<*iter<<end;
}
//反向迭代器方式
set<int>::reverse_iterator  riter;
for(riter = s.rbegin(); riter != s.rend(); riter++)
{
       cout<<*riter<<end;
}

自定义比较函数

使用insert()将元素插入到集合中去的时候,集合会根据设定的比较函数将该元素放到该放的节点上去。在定义集合的时候,如果没有
指定比较函数,那么采用默认的比较函数(按键值又小到大的顺序插入元素)。
编写比较函数的两张方法:
1. 如果元素不是结构体,那么可以编写比较函数。

//自定义比较函数myComp,重载操作符 ()  
struct myComp  
{  
    bool operator() (const int &a, const int &b)  
    {  
        return a > b;    //从大到小排序    
    }  
}; 

set<int, myComp> s1;

2.如果元素是结构体,那么可以直接把比较函数写在结构体内。

struct Info  
{  
    string name;  
    float score;  
    //重载操作符<,自定义排序规则 
    //注意这里有两个const
    bool operator< (const Info &a)const  
    {  
        return a.score > score;  //由小到大排列  
    }  
};  

set<Info> s;  

4. multiset

multiset同样在头文件里被定义,它和set的不同在于它可以允许重复的元素。
multiset的方法和set相同,唯一的区别就是multiset可以储存很多相同的值。
这里写图片描述

5. list、set和map的区别和联系

三者都是有序的。

list和set都是单列集合,他们有一个共同的父接口—collection。list是依次列出一个结合中的所有的元素,若集合中有重复的,同样列出,有序;set列出的集合中是不允许有重复的,也就是说里面有一个A对象和一个B对象,若是A.equals(B)==ture,那么用set方法列出的只会有一个,set集合默认是有序的,正向排序。一般遍历set里面的元素时使用iterator。
map <key,value> 是一个双列的集合,里面有一个key,和一个value,其中value是真正存储值的变量。map存储的数据也是有序的,根据键(key)排序。其键(key)不能重复,但是其值(value)是可以重复的。
比如说:
你需要存某门课程全班同学的成绩,就需要用到map,因为每个人的键(姓名)不一样,值(成绩)也不一样。而如果你要统计这门课的作业提交情况,就可以用set,因为你只需要存储键(姓名),一个同学只有交了(true)和没交(false)两种情况。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值