zoukankan      html  css  js  c++  java
  • 《C++ Primer》读书笔记—第十一章 关联容器

    声明:

    • 文中内容收集整理自《C++ Primer 中文版 (第5版)》,版权归原书所有。
    • 学习一门程序设计语言最好的方法就是练习编程

    一、使用关联容器

    1、关联容器中的元素按关键字保存和访问,顺序容器中的元素则按照它们在容器中的位置来顺序保存访问。关联容器支持高效的关键字查找和访问。

    2、两个主要的关联容器是map和set

      map中的元素是一些关键字值对:关键字起索引作用,值表示与索引相关联的数据。

      set中每个元素包含一个关键字;set支持高效的关键字查询操作--检查一个指定的关键字是否在set中。

    3、两个主要的关联容器是map和set,细分为8种

    • 有序
      • 关键字不可重复
        • map
        • set
      • 关键字可重复
        • multimap
        • multiset
    • 无序
      • 关键字不可重复
        • unordered_map
        • unordered_set
      • 关键字可重复
        • unordered_multimap
        • unordered_multiset

    4、使用map进行单词计数程序

     1 map<string, size_t> word_count;  //string到size_t的空map
     2 string word;
     3 while(cin >> word)
     4 {
     5     ++word_count[word];//提取Word的计数器并加1
     6 }
     7 
     8 for(const auto &w : word_count)
     9 {
    10     cout << w.first << " occurs " << w.second << "time(s)" << endl;
    11 }

      map中元素的关键字是string类型,值是size_t类型,当对word_count进行下标操作时,可以使用一个string作下标,获得与此string相关联的size_t类型的计数器。

    5、课后11.3题,自己写单词计数程序:

     1 #include<iostream>
     2 #include<string>
     3 #include<fstream>
     4 #include<vector>
     5 #include<map>
     6 using namespace std;
     7 
     8 int main(int argc, char**argv)
     9 {
    10     //map的定义
    11     map<string,size_t> word_count;
    12     fstream in("1.txt");//定义一个输入流
    13     string word;
    14 
    15     while (in>>word)
    16     {
    17         ++word_count[word];
    18     }
    19 
    20     //map同样支持迭代器操作
    21     map<string ,size_t>::iterator mapi;
    22     for (mapi = word_count.begin(); mapi != word_count.end(); ++mapi)//C++ 11支持:const auto &s : word_count
    23     {
    24         //两个成员分别代表关键字和对应值
    25         cout<<mapi->first<<" ";
    26         cout<<mapi->second<<" "<<endl;
    27     }
    28 
    29     return 0;
    30 }

    6、

     1 map<string, size_t> word_count;  
     2 set<string> exclude = {"the", "but"};  
     3 
     4 string word;  
     5 
     6 while(cin >> word)  
     7 {  
     8     if( exclude.find(word) == exclude.end() )  //只统计不在exclude中的单词
     9     {  
    10         ++word_count[word];  //获取并递增word的计数器
    11     }  
    12 }  
    13 
    14 for(const auto &w : word_count)  
    15 {  
    16     cout << w.first << " occurs " << w.second << "time(s)" << endl;   
    17 }

    二、关联容器概述

    1、map既需要关键字类型又需要指明值类型。set只需要指明关键字类型,因为set没有值。每个关联容器都定义一个默认构造函数,可以创建一个指定的空容器。既可以把关联容器初始化为另一个同类型容器的拷贝,或是从一个值范围来初始化关联容器,只要这些值能转化为容器所需的类型即可。在新标准下,可以对容器值初始化。

    1 set<string> exclude = {"the", "but"};  
    2 map<string, string> authors = { {"joyce", "james"},   
    3     {"austen","jane"},  
    4     {"dickens","charies"} };

    2、multimap和multiset可以有多个元素具有相同的关键字:

     1 vector<int> ivec;  
     2 for( vector<int>::size_type i = 0; i != 10; ++i )  
     3 {  
     4     ivec.push_back(i);  
     5     ivec.push_back(i);  //每个数重复保存一次
     6     //ivec: 0 0 1 1 2 2 ...  
     7 }  
     8 
     9 set<int> iset(ivec.cbegin(), ivec.cend());  
    10 multiset<int> miset(ivec.cbegin(), ivec.cend());
    11 cout<<ivec.size()<<endl;//打印20
    12 cout<<iset.size()<<endl; //打印10
    13 cout<<miset.size()<<endl;//打印20

    3、标准库使用<运算符比较两个关键字。也可以自定义一个比较操作,但必须是在关键字类型上定义一个严格弱序(strict weak ordering)。

    4、对于自定义的关键字类型,我们需要提供比较函数,并且需要在定义关联容器时就指定。

      从关联容器定义的构造函数也可以看出这一点。

    5、pair保存两个数据成员,定义在头文件utility中。创建一个pair时,必须提供两个类型名,pair的数据成员将具有对应的类型,两个类型不一定一样。

    1 pair<string,string> anon  //两个string
    2 pair<string, size_t> word_count;  //保存一个string和一个size_t
    3 pair<string, vector<int>> line;  //保存string和vector<int>

    可以提供初始化器:

    1 pair<string,string> author{"James","Joyce"};

    6、pair的数据成员是public的。分别命名为first和second。

    7、对pair的返回值进行列表初始化:

    1 pair<string, int>
    2 process(vector<string> &v)
    3 {
    4     if(!v.empty())
    5         return {v.back(),v.back().size()};//列表初始化
    6     else    
    7         return pair<string, int>();//隐式构造返回值
    8 }
    9 }

    三、关联容器操作

    1、关联容器额外的类型名

    • key_type    关键字类型
    • mapped_type    map值类型
    • value_type    对于set,与key_type相同。对于map,为pair<const key_type,mapped_type>

    只有map类型才定义了mapped_type。

    2、解引用关联容器的迭代器,会得到一个类型为容器的value_type的值的引用。set的关键字只能是const的。只能读,不能改。

    3、遍历关联迭代器,循环打印单词计数程序:

    1 //获得一个指向首元素的迭代器
    2 auto map_it = word_count.cbegin();  
    3 //比较当前迭代器和尾后迭代器
    4 while( map_it != word_count.cend() )  
    5 {  
    6     //解引用迭代器,打印关键字-值对
    7     cout << map_it->first << " occurs " << map_it->second << " times " << endl;  
    8     ++map_it;  //递增迭代器,移动到下一个元素
    9 }  

    4、通常不对关联容器使用泛型算法,关键字是const这一特性意味着不能将关联容器传递给修改或重排容器元素的算法。可用于只读算法,但很多这类算法都要搜索序列。关联容器使用find成员,通过给定关键字直接获取元素。所以不使用泛型的find。

    5、添加元素。insert成员可以向容器中添加一个元素或一个元素范围。

      有两个版本,接收一个迭代器或者一个初始化列表。

    1 vector<int> ivec = { 2, 4, 2, 4 };  
    2 set<int> set2;  
    3 set2.insert( ivec.begin(), ivec.end() );  
    4 set2.insert( { 1, 3, 1, 3 } );  

    6、对map进行insert操作时,必须记住元素类型是pair

    1 word_count.insert({word, 1});  
    2 word_count.insert(make_pair(word,1));  
    3 word_count.insert(pair<string,size_t>(word,1));  
    4 word_count.insert(map<string,size_t>::value_type(word,1));  

    7、对于不包含重复关键字的容器,insert返回一个pair。它的first成员是一个迭代器,指向具有给定关键字的元素,second成员是一个bool值,如果关键字已经在容器中,返回一个false。

    用insert重写单词计数程序:

     1 map<string, size_t> word_count;  
     2 string word;  
     3 while(cin >> word)  
     4 {  
     5     //插入一个元素,若关键字等于word,则值为1
     6     //若word已在word_count中,insert什么都不做
     7     auto ret = word_count.insert({word, 1});  
     8     if(!ret.second)  
     9     {  
    10         ++ret.first->second;  
    11     }  
    12 }  

    8、对允许重复关键字的容器,insert返回一个指向新元素的迭代器。无须返回一个bool值,因为总是会插入新元素。

    1 multimap<string, string> authors;  
    2 authors.insert({"author1", "book1"});  
    3 authors.insert({"author1", "book2"});  

    9、删除元素:

      可以传递给erase一个迭代器或者迭代器对,来删除一个元素或一个元素范围。与顺序容器相类似,指定的元素被删除,函数返回void。

      提供一个额外的erase操作,接受一个key_type参数,此版本删除所有匹配给定关键字的元素,返回实际删除元素的数量。可以用此版本在打印结果之前从word_count中删除一个元素。

    10、map的下标操作:

      仅map和unordered_map提供了下标操作

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

    1 c[k] 若关键字不在容器中会添加   
    2 c.at(k) 若关键字不在容器中会抛异常    

         map的下标操作返回的是一个mapped_type对象。

       下标运算符可能插入一个新元素,只可以对非const的map使用下标操作。

    11、下标和at操作只适用于非const的map和unordered_map,且下标在元素不存在时会执行插入操作,有时这并不是我们想要的。

      关联容器提供了其他查找指定元素的方法:

    • c.find(k)     返回一个迭代器,指向第一个关键字为k的元素
    • c.count(k)     返回关键字等于k的元素的数量  对于不允许重复关键字的容器,返回值永远是0或者1
    • c.lower_bound(k)    返回一个迭代器,指向第一个关键字不小于K的元素
    • c.upper_bound(k)   返回一个迭代器,指向第一个关键字大于K的元素
    • c.equal_range(k)    返回一个迭代器pair,表示关键字等于k的元素的范围。若K不存在,pair的两个成员均等于c.end();

      find返回一个迭代器,指向第一个关键字为k的元素,若k不存在,则返回尾后迭代器。

      lower_bound和upper_bound不适用于无序容器。

    12、如果一个multimap或multiset中有多个元素具有给定关键字,则这些关键字在容器中相邻存储。

    13、如果想把具有同一关键字的所有元素都打印出来,最直观的方法是使用find和count。

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

    14、lower_bound返回的迭代器指向第一个具有给定关键词的元素,upper_bound返回的迭代器指向最后一个匹配给定关键字之后的位置。如果元素不在multimap中,则lower_bound和upper_bound返回相等的迭代器,指向一个不影响排序的关键字插入位置。因此,使用相同的关键字调用lower_bound和upper_bound会得到一个迭代器范围,表示所有具有该元素关键字的范围。如果两者返回相同的迭代器,则表示关键字不在容器中。

    1 for(auto beg = authors.lower_bound(search_item),
    2 end = authors.upper_bound(search_item);
    3 beg != end; ++beg)  
    4 {  
    5     cout << beg->second << endl;  
    6 } 

    15、equal_range函数:接受一个关键字,返回一个迭代器pair,若关键字存在,则第一个迭代器指向第一个与关键字匹配的元素,第二个迭代器指向最后一个匹配元素之后的位置。若未找到匹配元素,则两个迭代器都指向关键字可插入的位置。

    1 //pos保存迭代器时,表示与关键字匹配的元素范围
    2 for(auto pos = authors.equal_range(search_item); pos.first != pos.second; ++pos.first)  
    3 {  
    4     cout << pos.first->second << endl;  
    5 }  

    四、无序容器

    1、有序容器通过比较运算符来组织元素,无序容器通过哈希函数和==运算符来组织元素。

    2、可以使用无序容器替换对应的有序容器,但由于元素未按顺序存储,一个使用无序容器的程序的输出通常与有序容器不同。

    用unorder_map重写最初的单词计数程序:输出单词的顺序不会按照字典排序

      unorder_map<string, size_t> word_count;  //string到size_t的空map
      string word;
      while(cin >> word)
     {
         ++word_count[word];//提取Word的计数器并加1
      }  
      
      for(const auto &w : word_count)
      {
         cout << w.first << " occurs " << w.second << "time(s)" << endl;
     }

    3、为了访问一个元素,容器首先计算元素的哈希值,它指出应该搜索哪个桶。

      哈希函数:将给定类型的值映射到一个整形值的函数,相同的值必须映射到相同的整数,不同的值尽可能映射到不同的整数。

      如果容器允许重复关键字,则所有具有相同关键字的元素在同一个桶中。无序容器的性能依赖于哈希函数的质量和桶的数量和大小。

      如果一个桶中保存了很多元素,需要顺序搜索这些元素来查找我们需要的那个,那么查找一个特定元素就需要大量比较操作。

    4、无序容器管理操作:

    桶接口

    • c.bucket_count()    正在使用的桶的数量
    • c.max_bucket_count()   容器能容纳的最多的桶的数量
    • c.bucket_size(n)  第n个桶中有多少个元素
    • c.bucket(k)  关键字k的元素在哪个桶中

    桶迭代

    • local_iterator   可以用来访问桶中元素的迭代器类型
    • const_local_iterator    桶迭代器的const版本
    • c.begin(n), c.end(n)   桶n的首元素迭代器和尾后迭代器
    • c.cbegin(n), c.cend(n)   与前两个函数类型,但返回const_local_iterator  

    哈希策略

    • c.load_factor()   每个桶的平均元素数量  返回float
    • c.max_load_factor()  
    • c.rehash(n)
    • c.reserve(n)  重新存储,使得c可以保存n个元素且不必rehash

    5、类似于为有序容器提供比较操作,我们需要为无序容器提供hash值计算函数和相等判断函数。

    附:一个单词转换的map:

    给定一个string,转化为另一个string。输入两个文件,第一个保存一些规则,用来转换第二个文件中的文本。每条规则由两个部分组成:一个可能出现在输入文件中的单词和一个用来替换他的短语。每当一个单词出现时,就把他替换成对应的短语。第二个文件包含要转换的文本。

      3.20:下午2.30开会到6.30。师兄们汇报研究成果,给我们分配任务。要求暑假前出文章。亚历山大。

      3.21:大四毕设要做DPC的相关,之前没做过这方面。给他整理了之前参考资料。源码也可以给他,本科毕业应该不难吧。别太烦我就行,指导反而更费时间。

          P391,一个单词替换的map还没有看,很有用的一个应用。

            第十一章结束,且随疾风前行。

  • 相关阅读:
    ThreadLocal全面解析,一篇带你入门
    StringTable字符串常量池的垃圾回收跟踪案例
    air镶边引7yue
    性能优化与团队效率
    air 错误信息一览
    AS3 使用unloadAndStop()卸载加载的swf以及里面的声音
    flash/flex/as3应用程序加载as2、as1版本的swf遇到的问题
    查看swc的代码
    chart 属性
    flex动态控制 effect
  • 原文地址:https://www.cnblogs.com/zlz099/p/6589361.html
Copyright © 2011-2022 走看看