zoukankan      html  css  js  c++  java
  • c++基础(五)——关联容器

    1.关联容器

      关联容器中的元素时按照关键字来保存和访问的,与之相对的,顺序容器中的元素时按它们在容器中的位置来顺序保存和访问的。两个主要关联容器是 map 和 set。标准库提供了8个关联容器,这8个容器间的不同体现在
    三个维度上:

    • 或是一个 map 或是一个 set
    • 或者要求不重复关键字,或者允许重复关键字
    • 按顺序保存元素,或无序保存

    2.使用关联容器

    • map
    • 1 //统计每个单词在输入中出现的次数
      2 map<string, size_t> word_count;    //string到size_t的空map
      3 string word;
      4 while (cin >> word)
      5     ++word_count[word];    //提取word的计数器并将其加1
      6 for(const auto &w : word_count)    //对map中的每个元素
      7     //打印结果
      8     cout << w.first << " occurs " << w.second
      9          << ((w.second > 1)? " times ": "time") << endl;
    • set
    • 1 //统计输入中每个单词出现的次数
      2 map<string, size_t> word_count;    //string到size_t的空map
      3 set<string> exclude = {"The", "But", "And", "Or", "An", "A",
      4                "the", "but", "and", "or", "an", "a"};
      5 string word;
      6 while (cin >> word)
      7     //只统计不在exclude中的单词
      8     if (exclude.find(word) == exclude.end())
      9         ++word_count[word];    //获取并递增word的计数器

    3.关联容器概述

      关联容器不支持顺序容器的位置相关操作,例如push_front或push_back。原因是关联容器中元素是根据关键字存储的,这些操作对关联容器没有意义。关联容器的迭代器都是双向的

    • 初始化 multimap 和 multiset。一个map或set中的关键字必须是唯一的,容器multimap和multiset没有此限制,它们允许多个元素具有相同的关键字。
    •  1 //定义一个有20个元素的vector,保存0到9每个整数的两个拷贝
       2 vector<int> ivec;
       3 for (vector<int>::size_type i = 0; i != 10; ++i){
       4     ivec.push_back(i);
       5     ivec.push_back(i);    //每个数重复保存一次
       6 }
       7 //iset包含来自ivec的不重复的元素,miset包含所有20个元素
       8 set<int> iset(ivec.begin(), ivec.end());
       9 multiset<int> miset(ivec.begin(), ivec.end());
      10 cout << ivec.size() << endl;    //打印出20
      11 cout << iset.size() << endl;    //打印出10
      12 cout << miset.size() << endl;    //打印出20

     3.1 set,map自定义函数排序

      关联容器对其关键字类型有一些限制。对于有序容器,关键字类型必须定义元素比较的方法。默认情况下,标准库使用关键字类型<运算符来比较两个关键字。

      用来组织一个容器中元素的操作的类型也是该容器类型的一部分。为了指定使用自定义的操作,必须在定义关联容器类型时提供此操作的类型使用方法如下:用尖括号指定要定义的那种类型的容器,自定义的操作类型必须在尖括号中紧跟元素类型给出。例如:我们不能直接定义一个Sales_data的multiset,因为Sales_data没有<运算符。因此需要定义一个compareIsbn:

    1 bool compareIsbn (const Sales_data &lhs, const Sales_data &rhs)
    2 {
    3     return lhs.isbn() < rhs.isbn();
    4 }

      为了使用自己定义的操作,在定义multiset时我们必须提供两个类型:关键字类型Sales_data,以及比较操作类型——应该是一种函数指针类型,可以指向compareIsbn。

    1 //bookstore中多条记录可以有相同的ISBN
    2 //bookstore中的元素以ISBN的顺序进行排列
    3 multiset<Sales_data, decltype(compareIsbn)*> bookstore(compareIsbn);

      此处,我们使用decltype来指出自定义操作的类型。记住,当用decltype来获得一个函数指针类型时,必须加上一个*来指出我们要使用一个给定函数类型的指针;用compareIsbn来初始化bookstore对象,这表示当我们想bookstore添加元素时,通过调用compareIsbn来为这些元素排序。

      下面举两个实例:均按照降序进行排列,容器默认是使用“<”。

     1 #include <set>
     2 #include <map>
     3 #include <vector>
     4 #include <string>
     5 #include <iostream>
     6 using namespace std;
     7 
     8 class st {
     9 public:
    10     int id;
    11     string name;
    12     st(int id, string name);
    13     ~st() {};
    14 };
    15 
    16 st::st(int id, string name) :id(id), name(name) {
    17 
    18 }
    19 
    20 bool compareST(const int &s, const int &r) {
    21     return s > r;
    22 }
    23 
    24 bool compareST2(const st *s, const st *r) {
    25     return s->id > r->id;
    26 }
    27 
    28 int main()
    29 {
    30     /// map
    31     st *st1 = new st(3, "st1");
    32     st *st2 = new st(1, "st2");
    33     st *st3 = new st(2, "st3");
    34     
    35     map<int, st*, decltype(compareST)*> temp(compareST);
    36     temp.insert(make_pair(st1->id, st1));
    37     temp.insert(make_pair(st2->id, st2));
    38     temp.insert(make_pair(st3->id, st3));
    39 
    40     for (auto item : temp) {
    41         cout << item.first << "	" << item.second->name << endl;
    42     }
    43 
    44     cout << "========================" << "
    ";
    45 
    46     /// set
    47     set<st*, decltype(compareST2)*> temp2(compareST2);
    48     temp2.insert(st1);
    49     temp2.insert(st2);
    50     temp2.insert(st3);
    51 
    52     for (auto item : temp2) {
    53         cout << item->id << "	" << item->name << endl;
    54     }
    55     
    56     getchar();
    57     return 0;
    58 }

    3.2 pair 类型

      它定义在头文件utility中。类似容器,pair是一个用来生成特定类的模板,与其他标准库类型不同,pair的数据成员是public的。两个成员分别命名为firstsecond,我们用普通的成员访问符号来访问它们。

      如下是一个错误写法:

    1 map<int, string> temp;
    2 pair<int, string> p(); // 显得不伦不类了。①不加括号,即采用第一种进行值初始化②采用(1,"test")利用第二种方式初始化
    3 temp.insert(p);

      改正:

    1 map<int, string> temp;
    2 pair<int, string> p(1, "dd");
    3 pair<int, string> p2;
    4 temp.insert(p);
    5 temp.insert(p2);
    6 temp.insert(make_pair(1, "dd"));

    3.3 关联容器的操作

      关联容器额外的类型别名:

      对于 set 类型,key_type 和 value_type 是一样的:set 中保存的就是关键字。

      在 map 中,元素是关键字-值对,即每一个元素是一个 pair 对象,包含一个关键字和一个关联的值,同时因为不能改变一个元素的关键字,因此这些 pair 的关键字部分是 const 的。

    1 map<int, string>::key_type v; // v 是一个 int
    2 map<int, string>::mapped_type v2; // v 是一个 string
    3 set<string>::key_type s; // s 是一个 string
    4 set<string>::value_type s2; // s 是一个 string 
    5 map<int, string>::value_type v3;// 是一个 pair<int, string> 类型

     4. 关联容器迭代器

      map 中的 key 值是const的,不能够被改变。

    1 map<int, string> m;
    2 m.insert({ 1, "a" });
    3 m.insert({ 2, "b" });
    4 for (auto iter = m.begin(); iter != m.end(); ++iter) {
    5    cout << iter->first << iter->second<<endl;
    6 }

      set的迭代器是const的。set 中的值 也只能访问不能修改。

    4.1 添加元素

      由于map和set包含不重复的关键字,因此插入一个已存在的元素对容器没有任何影响:

      注:insert有两个版本分别接受一对迭代器,或是一个初始化器列表,这两个版本的行为类似对应的构造函数。

    1 vector<int> ivec = { 2, 4, 6, 8, 2,4, 6, 8 };
    2 set<int> set2;
    3 set2.insert(ivec.begin(), ivec.end());    //set2有4个元素

    4.1.1 向 map 添加元素

    1 //向word_count插入word的4中方法
    2 word_count.insert({word, 1}); // 最简单
    3 word_count.insert(make_pair(word, 1));
    4 word_count.insert(pair<string, size_t>(word, 1));
    5 word_count.insert(map<string, size_t>::value_type(word, 1)); //构造一个恰当的 pair 类型,并构造该类型的一个对象,插入到map中

    4.1.2 检测 insert 的返回值

      insert(或emplace)返回的值依赖于容器类型和参数。对于不包含重复关键字的容器,添加单一元素的insert和emplace版本返回一个pair,告诉我们插入操作是否成功pair的first成员是一个迭代器,指向具有给定关键字的元素;second成员是一个bool值,指出元素是插入成功还是已经存在于容器中。

    4.1.3 删除元素

      关联容器定义了三个版本的erase,与顺序容器一样,我们可以通过传递给erase一个迭代器或一个迭代器对来删除一个元素或一个元素范围;关联容器还提供一个额外的erase操作,它接受一个key_type参数。

    4.1.4 map 的下标操作

      map和unordered_map支持下标运算和一个对应的 at 函数,但是 set 不支持,因为set中本身存放的就是 关键字。

     

      与其他下标运算符不同的是,但是如果关键字并不在map中,会为它创建一个元素并插入到map中,关联值将进行初始化。比如下面的例子:

    1     map<int, int> m;
    2     m.insert({ 1, 1 });
    3     m.insert({ 2, 2 });
    4 
    5     cout << m.size() << endl; // 2
    6     cout << m[3] << endl; // 0. key=3 的值不存在,插入并初始化
    7     cout << m.size() << endl; // 3
    8     cout << m[3] << endl; // 0    

     4.2 访问元素

      如果我们所关心的只不过是一个特定元素是否在容器中,可能find是最佳选择。

    1 set<int> iset = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
    2 iset.find(1);    //返回一个迭代器,指向key==1的元素
    3 iset.find(11);    //返回一个迭代器,其值等于iset.end()
    4 iset.count(1);    //返回1
    5 iset.count(11);    //返回0

      

      map 中利用 find 代替下标操作。当我们不想改变 map 的时候,下标操作会带来麻烦。

    1 if (word_count.find("foobar") == word_count.end())
    2     cout << "foobar is not in the map " << endl;

      在multimap或multiset中查找元素。如果 multimap 中有多个元素具有给定的关键字,这些元素会在容器中相邻存储

    1 string search_item("Alain de Botton");    //要查找的作者
    2 auto entries = authors.count(search_item);    //元素的数量
    3 auto iter = authors.find(search_item);    //此作者的第一本书
    4 //用一个循环查找此作者的所有著作
    5 while(entries){
    6     cout << iter->second << endl;    //打印每个题目
    7     ++iter;                //前进到下一本书
    8     --entries;            //记录已经打印了多少本书
    9 }

      

  • 相关阅读:
    webpack中的extract-text-webpack-plugin插件使用方法总结
    vue知识总结第一篇vue组件的定义以及父子组件的传值。
    Java在当前时间的基础上增加N时间
    SVN服务器端和客户端的搭建与使用
    字符串后面去0、补0
    安装Oracle数据库并建立可以用的用户
    【品优购代码外笔记】安装并使用Zookeeper
    【十次方基础教程(后台)】influxDB、cAdvisor、cAdvisor的安装与使用(监控微服务内存,自动扩容)
    【十次方基础教程(后台)】使用Rancher管理容器
    【十次方基础教程(后台)】使用Gogs,Jenkins实现持续集成
  • 原文地址:https://www.cnblogs.com/KongHuZi/p/11366383.html
Copyright © 2011-2022 走看看