zoukankan      html  css  js  c++  java
  • 关联容器小结

    关联容器和顺序容器的不同

    关联容器和顺序容器的根本不同之处在于,关联容器中的元素是按关键字来保存和访问的(比如map和set),而顺序容器中的元素是按照在容器中的位置来顺序保存和访问的(比如vector和string)。

    顺序容器有

    vector
    deque
    list
    forward_list
    array
    string
    

    关联容器有

    按照关键字有序保存
    map
    set
    multimap
    multiset
    无序保存
    unordered_map
    unordered_set
    unordered_multimap
    unordered_multiset
    

    关联容器不支持和位置相关的操作,因为是按关键字顺序存储的,关联容器的迭代器都是双向的。

    对于有序关联容器中的关键字类型要求

    对与有序关联容器而言,关键字类型必须定义元素比较的方法(这一点尤其重要),默认时,使用关键字类型的<运算符来比较两个关键字。即虽然可以向容器中保存自己定义的类或者结构体,但是如果这些类和结构体中没有定义<运算符的话,则该行为就是不合法的。

    但是可以向算法提供一个自己定义的比较操作(通常是一个函数)或者对<进行重载来完成比较操作,唯一需要注意的就是,所提供的操作必须在关键字类型上严格弱序(可以看作小于等于)

    严格弱序具有反自反性(不能同时小于等于对方),也具有传递性(k1<=k2,k2<=k3则有k1<=k3)

    严格弱序上的等价关系是任何一个都不小于等于另一个,这个等价关系也具有传递性。

    在使用自己定义的操作时,必须要提供该操作类型(可以使用decltype来获得函数指针类型),比如定义一个multiset

    multiset<type,decltype(CompareFunction)*>name(CompareFunction);
    

    关联容器额外的类型别名

    类型别名 含义
    key_type 该容器的关键字类型
    mapped_type 关键字关联的类型,只适用于map
    value_type 对于set而言和key_type相同;对于map,为pair<const key_type,mapped_type>

    关联容器和算法

    实际使用算法时,关联容器只能是一个源序列或者目的序列。
    比如:

    	multiset<string>c{ "1","2","1" };
    	vector<string>v;
    	copy(c.begin(), c.end(), back_inserter(v));//v为目的序列,c为源序列
    
    	multiset<string>c;
    	vector<string>v{ "1","2","1" };
        copy(v.begin(), v.end(), inserter(c,c.end())); //c为目的序列,v为源序列
    

    关联容器的操作及其返回值

    插入元素

    insert向容器中插入一个元素,不存在则什么也不做。emplace也是插入元素,但是只有当关键字不在容器时才插入。

    insert(value_type);  //value_type对于set就是关键字,对于map而言就是一个pair
    insert(args);		//arges时一个初始化列表
    insert(begin,end);	//insert也可以接受一对迭代器begin,end;
    insert(p,args);		//从容器的p位置开始搜索新元素args的插入位置
    
    emplace(value_type);//当关键字不在容器时才插入
    emplace(p,args);//从容器的p位置开始搜索新元素args的插入位置
    

    对于插入一个元素的insert(value_type)emplace(value_type)会返回一个pair< value_type::iterator,bool >,

    pair的first时一个指向给定插入元素的迭代器,pair的second是一个表示是否插入成功的布尔值;

    对于insert(args)insert(begin,end),返回一个void;

    对于insert(p,args)emplace(p,args),返回一个迭代器,指向给定元素。

    删除元素

    关联容器有三个版本的erase操作,分别接受一个关键字,一个迭代器和一对迭代器。

    访问和查找元素

    1.下标(有序无序可用)

    对于map而言,可以使用下标或者一个at函数来对容器中的元素进行访问,比如m[key]或者m.at(key)这样的操作。
    对于下标而言,如果该关键字在容器中不存在,那么就会被当作一个新元素加入到容器中去。at函数没有这样的作用,它只会对参数进行检查,如果不存在就抛出一个out_of_range的异常。

    而对于set而言,是不能用下标进行访问的。

    2.find或者count(有序无序可用)

    对于map和set都有自带的find和count版本,find会返回一个迭代器,指向第一个关键字为参数的元素,但如果该关键字不存在,就返回尾后迭代器。
    count会返回容器中符合要求的关键字的数量,不存在则为0;

    3.lower_bound和upper_bound(有序版本)

    lower_bound(k)会返回一个迭代器,指向第一个关键字不小于k的元素(即<k的上界,>=k的下界)。如果不存在则返回一个不影响排序的关键字的插入位置(即如果要将这个不存在的关键字插入到容器中时要放入的位置)
    upper_bound(k)会返回一个迭代器,指向第一个关键字大于k的元素,即>k的下界,<=k的下界。如果不存在也会返回一个不影响排序的关键字的插入位置
    比如:

    multimap<string,string>words;
    string k="Test";
    auto b=words.lower_bound(k),e=words.upper_bound(k);
    for(;b!=e;++b)cout<<b->second<<endl;
    

    如果调用lower_bound(k)的话,如果容器中存在关键字k的话,b就会匹配到第一个k所在的元素位置,e则会指向最后一个k之后的位置,否则b和e都会指向一个相同位置,即不影响排序的关键字的插入位置
    所以对于同样的参数调用,如果lower_bound和upper_bound指向的迭代器不相同,那么两个就是一个包含所有关键字k的迭代器范围[b,e)

    4.equal_range函数(有序版本)

    对于上面的版本,用equal_range函数可以改写为:

    multimap<string,string>words;
    string k="Test";
    auto pos=words.equal_range(k);
    for(;pos.first!=pos.second;++pos.first)cout<<pos.first->second<<endl;
    
    //pos.first和words.lower_bound(k)相等
    //pos.second和words.upper_bound(k)相等
    

    equal_range接受一个关键字,返回一个pair,包含两个迭代器。当关键字存在时,两个迭代器就是刚才用lower_bound和upper_bound得到的迭代器。当不存在时,也返回不影响排序的关键字的插入位置

    无序容器

    概念和性能

    无序容器使用一个哈希函数和关键字类型的==运算符来组织元素,而不是<运算符。
    无序容器在存储上的组织形式为一组桶,利用哈希函数将具有一个相同哈希值的元素保存在相同的桶中(即使是重复版本的无序容器也是一样),所以无序容器的性能取决于哈希函数的性能和桶的数量和大小。

    无序容器对关键字类型的要求

    对于内置的类型比如int,以及标准库类型比如string,标准库都提供了默认的hash模板,所以可以直接定义,比如:

    unordered_map<string, int>words;
    

    但是对于自定义的类型,就需要提供hash模板或者提供函数来替代==运算符和哈希函数(类似重载)。
    一种特别的情况时,如果关键字是一个已经自定义了==运算符的类,则只需要提供哈希函数即可。

  • 相关阅读:
    一些你可能用到的代码
    iOS 键盘下去的方法
    iOS设计模式汇总
    随笔
    Spring cloud config 分布式配置中心 (三) 总结
    Spring cloud config 分布式配置中心(二) 客户端
    Spring cloud config 分布式配置中心(一) 服务端
    jdbcUrl is required with driverClassName spring boot 2.0版本
    JpaRepository接口找不到 spring boot 项目
    解决IntelliJ “Initialization failed for 'https://start.spring.io'
  • 原文地址:https://www.cnblogs.com/FlyerBird/p/11808668.html
Copyright © 2011-2022 走看看