STL容器学习第三篇(记录)关联容器

关联容器:

关联容器与顺序容器的区别 在于  关联容器 是通过key  来存取 和读取 元素,顺序容器通过 容器中的位置 顺序存储和访问元素。  关联容器 是通过key -value方式工作的 。


关联容器类型:map 和set  multimap 和 multiset    map是 key-value    set  是 只存放一个key。。   如字意表示:multimap  和 multiset 表示 可以存放 一个多次出现key


pair类型

  pair是一个简单的标准库类型,和内置类型int  类似。这个类型中含有 2个数据类型,

int main()
{
    pair< int, int > v;
    pair< string, string > v1( "taotaosb", "taotaosb " );
    pair< string, string > v2 = make_pair( "taotao sb", "haha" );
    //这样使用是比较麻烦的,可以使用typedef 声明
    typedef pair< string, string > Author;
    Author s( "hehe", "haha" );
    s.first = "sb";  //可以直接赋值
    cout << s.first << s.second << endl;
    cout << (v1 > v2) << endl;
    cout << (v1 == v2) << endl;
    cout << v1.first << " " << v2.second << endl;
}

pair 的 成员都是公有的,可以直接访问。  

make_pair(  ,  ) 可以生成一个pair对象。


关联容器:


map

关联容器 能使用 大部分  顺序容器操作。  不提供的 包括  front  push_front  back  等。 因为 关联容器要靠 key  来访问。不能以类似这种访问方式

构造函数:

不能通过容器大小来定义,否则无法知道key的类型。key必须 有一个能够实现比较的比较函数,键类型必须定义< 操作符 ,而且该操作符  必须能够“正确的工作",其他的关系或者相等运算,不做要求


map的类型:

map<k,v>::key_type                类型  是 const k类型

map<k,v>::mapped_type       map容器中,键所关联的值的类型  这里是 V类型

map<k,v>::value_type           pair类型,值成员可以修改,key成员不可以修改


map迭代器解引用就会产生一个 pair类型的对象。

给map添加 元素:

1) 使用 key下标 来 获取元素,然后给获取的元素赋值,      

  使用下标访问map对象的时候,如果不存在 这个元素将会在map容器中 添加一个新的元素,他的键就是该下标值。(如果存在就返回其值,如果不存在就插入一个新的元素),所关联的值 采用值初始化

 // 3种构造方式
    map< string, int >  word_count ;
    word_count["hehe"] = 1;

    map< string, int >::iterator map_it = word_count.begin();
    cout << (*map_it).first << " " << (*map_it).second << endl;
    cout << word_count["haha"];    //打印为0
我们在最后一句访问的时候, 首先在map中搜索,如果没有,就插入 一个新的元素,关联值采用值初始话 ,也就是0  
++word_count["haha"];  //修改值   map下标返回的类型就是 值的类型, 而map的迭代器 返回的类型 是pair类型。
访问下标所表示的建在容器中不存在,则添加新元素,这个 特性 很好用


 2)或者 采用insert成员

insert 中的参数 必须是  pair类型的 :  如果 参数的key不再  map中 则 插入,如果 存在,则 保持不变。该函数返回类型  是一个pair类型 ,包含一个map迭代器,和一个bool变量,表示是否插入了该元素。

 map< string, int >  word_count ;
    pair< string, int > S( "hehe", 5 );
    pair< map< string, int >::iterator, bool > ret;

    ret = word_count.insert(S);
    cout << (*(ret.first)).first  << endl;
    cout << ret.second  << endl;     //返回1,因为不存在,则插入成功
    ret = word_count.insert(S);
    cout << (*ret.first).first  << endl;
    cout << ret.second  << endl;  //返回0  因为 map中已经存在 ,不需要再次插入了
}
这中方式可以避免用下标那种方式中的 不必要的初始化。可以直接用make_pair节省  代码。

而用下标 和 insert  插入 有一个很重要的区别就是,用下标插入的时候,如果map中已经存在,则 更新与key对应的值,如果用insert  ,如果已经存在的话,则不更新。看下面代码:

 map< string, int >  word_count ;
    pair<  map< string, int >::iterator, bool > ret;

    ret = word_count.insert( make_pair( "hehe", 5 ) );
    cout << (*ret.first).first  << endl;
    cout << (*ret.first).second  << endl;
    word_count.insert( make_pair( "hehe", 7 ) );
    cout << (*ret.first).first  << endl;
    cout << (*ret.first).second  << endl;
    word_count["hehe"] = 7;

    cout << word_count["hehe"] << endl;
打印结果:


可以看出来区别

 insert(beg,end) 两个迭代器的 插入方式:

 map< string, int >  word_count ;
    typedef pair< string, int > Author;
    vector< Author > s;
    s.push_back( make_pair( "chenhao", 1 ) );
    s.push_back( make_pair( "taotao ", 2 ) );
    s.push_back( make_pair( "chenhao1", 3 ) );
    s.push_back( make_pair( "chenhao2", 4 ) );
    vector< Author >::iterator itBegin = s.begin();
    vector< Author >::iterator itEnd = s.end();

    word_count.insert( itBegin, itEnd);
    cout << word_count["chenhao"] << endl;
    


访问map中的元素

使用下标访问,这是比较危险的,因为 当你map中没有该元素的时候,会自动添加 该元素,并将 值 设置为默认初始值。有的时候我们不想插入元素,只向看看map中是不是存在该元素 。对于该类应用,不能采用下标来判断。则有两个函数可以选择  count(), 和find()

int main()
{
    // 3种构造方式
    map< string, int >  word_count ;
    typedef pair< string, int > Author;
    vector< Author > s;
    s.push_back( make_pair( "chenhao", 1 ) );
    s.push_back( make_pair( "taotao ", 2 ) );
    s.push_back( make_pair( "chenhao1", 3 ) );
    s.push_back( make_pair( "chenhao2", 4 ) );
    vector< Author >::iterator itBegin = s.begin();
    vector< Author >::iterator itEnd = s.end();

    word_count.insert( itBegin, itEnd);
    //返回 存在的个数, map中肯定只能为 1 或者为0
    cout <<  word_count.count( "chenhao" ) << endl;
    map< string, int >::iterator iter;
    //返回 一个指向该元素的 迭代器,如果不存在则返回超出末端迭代器,
    iter = word_count.find( "taotao " );
    cout << (*iter).first << endl;
    iter = word_count.find( "taotaohah " );
    cout << (*iter).first << endl;// 这里 是超出末端迭代器,这里解引用会出现错误,所以用的时候一定要小心

}
看下运行结果:



从map中删除元素
1)erase(key)//key为 键值
2)erase(p)  //p为指向map元素的一个迭代器

 // 3种构造方式
    map< string, int >  word_count ;
    typedef pair< string, int > Author;
    vector< Author > s;
    s.push_back( make_pair( "chenhao", 1 ) );
    s.push_back( make_pair( "taotao ", 2 ) );
    s.push_back( make_pair( "chenhao1", 3 ) );
    s.push_back( make_pair( "chenhao2", 4 ) );
    vector< Author >::iterator itBegin = s.begin();
    vector< Author >::iterator itEnd = s.end();
    word_count.insert( itBegin, itEnd);
    //删除map容器中的元素的3种方式
    cout << word_count.count( "chenhao" ) << endl;
    word_count.erase( "chenhao" );
    cout << word_count.count( "chenhao" ) << endl;

    map< string, int >::iterator iter = word_count.find("taotao ");
    cout << ( *iter ).first << endl;
    cout << word_count.count( "taotao " ) << endl;
    word_count.erase( iter );
    cout << word_count.count( "taotao " ) << endl;

打印输出:

1
0
taotao
1
0


3)erase(b,e) //b 和e都是指向 map中的两个迭代器 

int main()
{
    // 3种构造方式
    map< string, int >  word_count ;
    typedef pair< string, int > Author;
    vector< Author > s;
    s.push_back( make_pair( "chenhao", 1 ) );
    s.push_back( make_pair( "taotao ", 2 ) );
    s.push_back( make_pair( "chenhao1", 3 ) );
    s.push_back( make_pair( "chenhao2", 4 ) );
    vector< Author >::iterator itBegin = s.begin();
    vector< Author >::iterator itEnd = s.end();
    word_count.insert( itBegin, itEnd);
    map< string, int >::iterator iter1 = word_count.find( "chenhao" );
    map< string, int >::iterator iter2 = word_count.find( "chenhao2" );
    word_count.erase( iter1, iter2 );
    cout << word_count.count( "chenhao") << endl;
    cout << word_count.count( "taotao ") << endl;
    cout << word_count.count( "chenhao1") << endl;
    cout << word_count.count( "chenhao2") << endl;
}
看下打印的结果:

这是 什么原因呢,为什么  只删除了chenhao  和 chenhao1  这两个 元素 ,剩下的两个 为什么没删除呢,那么带着这个疑问 继续往下看。
map对象的迭代遍历
map对象一样 提供 begin 和  end 运算 ,以便生成遍历整个容器的迭代器,
int main()
{
    // 3种构造方式
    map< string, int >  word_count ;
    typedef pair< string, int > Author;
    vector< Author > s;
    s.push_back( make_pair( "chenhao", 1 ) );
    s.push_back( make_pair( "taotao ", 2 ) );
    s.push_back( make_pair( "chenhao1", 3 ) );
    s.push_back( make_pair( "chenhao2", 4 ) );
    vector< Author >::iterator itBegin = s.begin();
    vector< Author >::iterator itEnd = s.end();
    word_count.insert( itBegin, itEnd);

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

注意下打印结果:



注意下 我们可以看到map  容器 在插入的 时候 ,会自动排序 将 map中的元素 按照键的升序排列。。写个函数 验证下:

 // 3种构造方式
    map< string, int >  word_count ;
    typedef pair< string, int > Author;
    vector< Author > s;
    s.push_back( make_pair( "chenhao4", 1 ) );
    s.push_back( make_pair( "chenhao3", 1 ) );
    s.push_back( make_pair( "chenhao2", 1 ) );
    s.push_back( make_pair( "chenhao1", 1 ) );
    vector< Author >::iterator itBegin = s.begin();
    vector< Author >::iterator itEnd = s.end();
    word_count.insert( itBegin, itEnd);
    map< string, int >::iterator itsBegin = word_count.begin();
    while( itsBegin != word_count.end() )
    {
        cout << itsBegin->first << endl;
        itsBegin ++;
    }
打印结果:

chenhao1

chenhao2

chenhao3

chanhao4

OK,基本 满足要求。

使用  map 的 一个例子:  C++primer  318 的题目

#include <iostream>
#include <string>
#include <vector>
#include <list>
#include <deque>
#include <queue>
#include <stack>
#include <map>

//默认的queue  和 queue  通过deque容器实现的  priority_queue 是在vector上实现的
using namespace std;
int main()
{
    map< string, string >  trans_map ;
    trans_map.insert( make_pair( "'em", "them" ) );
    trans_map.insert( make_pair( "cuz", "because" ) );
    trans_map.insert( make_pair( "gratz", "grateful" ) );
    trans_map.insert( make_pair( "i", "I" ) );
    trans_map.insert( make_pair( "nah", "no" ) );
    trans_map.insert( make_pair( "pos", "supposed" ) );
    trans_map.insert( make_pair( "sez", "said" ) );
    trans_map.insert( make_pair( "tanx", "thanks" ) );
    trans_map.insert( make_pair( "wuz", "was" ) );

    string input;
    getline( cin, input );

    string word;
    string::size_type posBegin, posEnd = 0;
    string separtor( " " );

    map< string, string >::iterator itmap;
    while( ( posBegin = input.find_first_not_of( separtor, posEnd ) ) != string::npos )
    {
        posEnd = input.find_first_of( separtor, posBegin );
        if( posEnd == string::npos )
            word.assign( input.begin() + posBegin, input.begin() + input.size() );
        else
            word.assign( input.begin() + posBegin, input.begin() + posEnd );

        if( trans_map.count( word ) == 1)
        {
            itmap = trans_map.find( word );
            cout << ( *itmap ).second << " ";
        }
        else
            cout << word << " ";
    }
    cout << endl;
}
输入和输出:


OK,map先写 这么多。


set类型:

当只想知道 一个 值是否存在的时候,  使用set 容器是最合适的。  set  不支持 下标操作符。而且 没有 mapped_type 类型。  且 set容器中 存储的键  必须 唯一,且 不可以修改,而且值各不相同。

set  容器 满足 map 中的大部分操作

int main()
{
    vector< int > ivec;
    for( vector< int >::size_type i = 0; i != 10; i++ )
    {
        ivec.push_back( i );
        ivec.push_back( i );
    }
    //第一种构造方式
    set< int > iset( ivec.begin(), ivec.end() );

    //插入方式
    iset.insert( 10 );
    cout << iset.size() << endl;
    cout << ivec.size() << endl;
    int  data[] = { 11, 12, 13, 14};
    //第二种插入方式
    iset.insert( data, data + 4 );
    cout << iset.size() << endl;
    pair< set< int >::iterator, bool > pa;
    pa = iset.insert( 17 );
    cout << *( pa.first) << endl;
    cout << ( pa.second) << endl;
    //cout << iset.size() << endl;

}

insert 版本的  含有一个 键参数   仍然返回一个 pair 类型,  第一个成员是迭代器 ,第二个成员是  bool ,如果插入成功则返回真。


multimap  和 multiset


可以包含相同的一样的key  ,因为 每次 insert  都会添加一个元素,这也是 比较大的一个区别。
删除 的时候  如果 删除 一个键的话,将删除该键对应的所有元素,并返回删除个数,如果是删除迭代器,则返回void
int main()
{
    multimap< string, string > mmap;
    mmap.insert( make_pair( "hehe","haha ") );
    mmap.insert( make_pair( "hehe","heihei ") );
    mmap.insert( make_pair( "hehe","sb ") );;
    multimap< string, string>::iterator it = mmap.end();
    mmap.erase( --it );  //返回为void类型
    cout << mmap.erase( "hehe" ) << endl;
}

输出为: 2

在 multimap 和multiset 容器中,如果 某个键对应多个实例, 则这些 实例在容器中将相邻存放。

在 find  和 count  函数中,count 返回 个数, find  返回一个迭代器,指向 第一个实例。

那么如何 打印出来 一个键对应的所有元素呢,有3种方法,我们依次来看一下。

1)

int main()
{
    multimap< string, string > mmap;
    mmap.insert( make_pair( "hehe","haha ") );
    mmap.insert( make_pair( "hehe","heihei ") );
    mmap.insert( make_pair( "hehe","sb ") );;
    multimap< string, string >::iterator it;
    it = mmap.find( "hehe" );
    int n = mmap.count( "hehe" );
    for( int i = 0; i < n ; i++ ,it++)
    {
        cout << ( *it ).second << " ";
    }
    return 0;
}

利用 count返回的个数,一个一个的打印。

2) 特殊 的解决方案

lower_bound(k)  返回一个迭代器,指向键不小与k的第一个元素

upper_bound(k)  返回一个迭代器,指向键大于k的第一个元素

equal_range(k)返回一个迭代器的pair对象,第一个成员就是 lower_bound(k)  返回的迭代器,第二个成员就是 upper_bound(k)  返回的迭代器。

int main()
{
    multimap< string, string > mmap;
    mmap.insert( make_pair( "aaaa","haaa ") );
    mmap.insert( make_pair( "bbbb","bfsg ") );
    mmap.insert( make_pair( "hehe","haha ") );
    mmap.insert( make_pair( "hehe","heihei ") );
    mmap.insert( make_pair( "hehe","sb ") );
    mmap.insert( make_pair( "zzze","hsgsa ") );

    multimap< string, string >::iterator itLower;
    multimap< string, string >::iterator itUpper;
    typedef multimap< string, string >::iterator S;
    pair< S,S > itPair;
    itLower = mmap.lower_bound( "hehe" );
    itUpper = mmap.upper_bound( "hehe" );
    cout << itLower -> second << endl;
    cout << itUpper -> second << endl;
    return 0;
}
结果:

haha

hsgsa 






  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值