• c++primer笔记十一、关联容器


    与顺序容器的根本不同:关联容器中的元素是按关键字来保存和访问的,而顺序容器是按位置保存的
    关联容器支持高效的查找和访问
    主要的有map和set

    一共有8种,主要分类依据:
    1、set或map
    2、是否允许重复关键字,multi
    3、按顺序保存或无序,unordered_

    使用关联容器

    map:关联数组
    set:集合

    使用map

    例:单词计数

    //统计每个单词在输入中出现的次数
    map<string, size_t> word_count;
    string word;
    while (cin >> word)
        ++word_count[word];
    for (const auto &w : word_count)
        cout << w.first << " occurs " << w.second
             << ((w.second > 1) ? " times" : " time") << endl;
    

    从map中提取一个元素时,得到的是pair

    使用set

    例:对上一个程序中的常见单词进行忽略,可以用set保存忽略单词

    map<string, size_t> word_count;
    set<string> exclude = { "The", "But", "And", "Or", "An", "A",
                            "the", "but", "and", "or", "an", "a"};
    string word;
    while(cin >> word)
        if (exclude.find(word) == exclude.end())
            ++word_count[word];
    

    关联容器概述

    关联容器不支持位置相关的操作,都支持一些其他操作。
    还提供用来调整哈希性能的操作。
    迭代器都是双向的

    定义关联容器

    map<string, size_t> word_count;     //空容器
    //列表初始化
    set<string> exclude = { "The", "But", "And", "Or", "An", "A",
                            "the", "but", "and", "or", "an", "a"};
    //map初始化
    mat<string, string> authors = { {"joyce", "james"},{"Austem", "Jane"}};
    

    把关键字-值包围在花括号中

    multimap 或multiset

    允许拥有重复的关键字

    关键字类型的要求

    关键字类型必须定义元素比较的方法,默认使用<

    可以定义自己的操作来带起关键字上的<运算符,必须定义一个严格弱序("小于等于")

    例:为了使用自己定义的操作,必须提供两个类型:Sakes_data以及比较操作类型

    multiset<Sales_data, decltype(compareIsbn)*> bookstore(compareIsbn);
    
    pair类型

    定义在utility中,一个pair保存两个数据成员

    pair<string, string> anon;      //保存两个string
    

    pair的默认构造函数对数据成员进行值初始化,也可以提供初始化器

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

    pair的数据成员是public的,分别为first和second
    其它操作见表11.2

    创建pair对象的函数

    可以用花括号

    pair<string, int> process(vector<string> &v)
    {
        if(!v.empty())
            return {v.back(), v.back().size());
        else
            return pair<string, int>();
    }
    

    老版本不能用花括号,必须显式构造

    return pair<string, int>(v.back(), v.back().size());
    

    或者用make_pair

    return make_pair(v.back(), v.back().size());
    

    关联容器操作

    关联容器额外的类型别名

    key_type        此容器类型的关键字类型
    mapped_type     每个关键字关联的类型;只适用于map
    value_type      对于set,于key_type相同
                    对于map,为pair<const key_type, mapped_type>
    

    这些pair关键字部分是const的

    set<string>::value_type v1;         //v1是string
    set<string>::key_type v2;           //v2是string
    map<string, int>::value_type v3;    //V3是pair<const string, int>
    map<string, int>::key_type v4;      //v4是string
    map<string, int>::mapped_type v5;   //v5是int
    

    关联容器迭代器

    map的value_type是一个pair。只能改变值不能改变关键字

    map_it->first = "new";  //错误,关键字是const
    ++map_it->second;       //正确,元素可以改
    
    set的迭代器是const的

    iterator和const_iterator都是只读的

    遍历关联容器

    map和set都支持begin和end
    例:打印单词技术结果

    auto map_it = word_count.cbegin();
    while (map_it != word_count.cend())
    {
        cout << map_it->first << " occurs"
             << map_it->second << "times" << endl;
        ++map_it;
    }
    

    map、multimap、set、multiset的迭代器按关键字升序遍历元素

    关联容器和算法

    关联容器可用于只读取的算法,修改或重排的不行。set是const的,map中的pair第一个成员是const的。
    使用时要么把它当作一个源序列,要么当作一个目的位置。

    添加元素

    使用insert添加元素,如果插入一个已存在元素对容器也没有任何影响

    向map添加元素

    有4个方法

    word_count.insert({word,1});
    word_count.insert(make_pair(word, 1));
    word_count.insert(pair<string, size_t>(word, 1));
    word_count.insert(map<string, size_t>::value_type(word,1)); //自动构造合适的pair
    

    其它操作见表11.4

    检测insert的返回值

    insert(或emplace)的返回值依赖于容器类型和参数。
    对于不重复的容器,添加单一元素的insert返回一个pair,first是迭代器,指向给定关键字的元素;second是bool值,指出是插入成功还是以及存在于容器中。
    例:计数单词

    map<string, size_t> word_count;
    string word;
    while (cin >> word){
        auto ret = word_count.insert({word,1});
        if (!ret.second)
            ++ret.first->second;
    }
    
    展开递增语句
    ++((ret.first) -> second);
    
    向multiset或multimap添加元素

    允许重复关键字的容器,接受单个元素的insert操作返回一个指向新元素的迭代器。不需要返回bool

    删除元素

    通过erase删除,有3个版本。可以传递迭代器或迭代器对表示的范围来删除元素
    关联容器可以接受一个key_type参数,删除所有匹配给定关键字的元素,返回实际删除的元素的数量。

    map的下标元素

    map和unordered_map都有下标运算符和at函数,set不支持下标。
    下标接受索引,但如果下标不存在,会创建一个元素并插入到map中,关联值将进行值初始化。

    word_count["Anna"] = 1; //插入一个关键字值对并用1初始化

    at则是访问关键字为k的元素,如果不存在抛出异常

    访问元素

    对于不允许重复的容器find和count没区别
    允许重复的容器count会统计次数,如果不需要计数用find

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

    map还有其它操作

    c.lower_bound(k);       //指向第一个关键字不小于k的元素
    c.upper_bound(k);       //指向第一个关键字大于k的元素
    c.equal_range(k);       //返回一个迭代器pair,表示关键字等于k的元素的范围。若k不存在则两个成员都是c.end();
    
    对map使用find代替下标操作

    下标一个副作用:如果关键字不存在会创建一个
    如果只想知道一个给定的关键字是否在map中,而不想改变map,用find

    if (word_count.find("foobar") == word_count.end())
        cout << "foobar is not in the map" << endl;
    
    在multimap或multiset中查找

    一个容器中可能有很多元素会有给定的关键字,则这些元素会相邻存储。
    例:找一个特定作者的所有著作

    string search_item("Alain de Botton");
    auto entries = authors.count(search_item);  //返回元素数量
    auto iter = authors.find(search_item);      //返回第一本书
    while(entries)
    {
        cout << iter->second << endl;
        ++iter;
        --entries;
    }
    

    先用count获取数量,再用find获得第一个迭代器,进行循环

    一种面向迭代器的解决方法

    用lower_bound和upper_bound来解决
    如果关键字在容器,返回第一个元素迭代器和最后一个迭代器之后的位置
    如果关键字不在,都返回一个不影响排序的关键字插入位置
    如果查找的元素具有最多的关键字,upper_bound返回尾后迭代器。如果关键字不存在且大于容器中的任何关键字,则lower_bound也是尾后迭代器

    //重写上面
    for (auto beg = authors.lower_bound(search_item),
              end = authors.upper_bound(search_item);
         beg != end; ++beg)
         cout << beg->second << endl
    
    equal_range

    此函数接受一个关键字,返回一个迭代器pair。
    若关键字存在,则第一个迭代器指向关键字匹配元素,第二个迭代器指向最后一个匹配元素之后的位置。
    若不存在则都返回指向可以插入的位置

    //重写
    for (auto pos = authors.equal_range(search_item);
         pos.first != pos.second; ++pos.first)
         cout << pos.first->second << endl;
    
    一个单词转换的map程序

    给定一个string,根据对应表,转换成另一个string。
    程序使用三个函数,word_transform管理整个过程,接受两个ifstream:一个绑定到单词转换文件,一个绑定到我们要转换内容的映射。
    buildMap读取转换规则文件,并创建一个map,保存每个单词到其转换内容的映射。
    transform接受一个string,如果存在转换规则,返回转换后的内容

    void word_transform(ifsteam &map_file, ifstream &input)
    {
        auto trans_map = buildMap(map_file);
        string text;
        while (getline(input, text)) {
            istringstream stream(text);
            string word;
            bool firstword = true;
            while (stream >> word){
                if (firstword)
                    firstword = false;
                else
                    cout << " ";
                cout << transform(word, trans_map);
            }
            cout << endl;
        }
    }
    
    map<string, string> buildMap(ifsteam &map_file)
    {
        map<string, string> trans_map;
        string key;
        string value;
        while (map_file >> key && getline(map_file, value))
            if (value.size() > 1)
                trans_map[key] = value.substr(1);
            else
                throw runtime_error("no rule for " + key);
        return trans_map;
    }
    
    const string & transform(const string &s, const map<sting, string> &m)
    {
        auto map_it = m.find(s);
        if (map_it != m.cend())
            return map_it->second;
        else
            return s;
    }
    

    11.4 无序容器

    unordered_map、unordered_set
    新标准定义了4个无序关联容器,使用哈希函数和关键字类型得==运算符
    无序容器也能用有序容器相同得操作如(find、insert)。

    管理桶

    无序容器在存储上组织为一组桶,每个桶保存零或多个元素。使用哈希函数将元素映射到桶。
    访问时先计算元素得哈希值,将一个具有特定哈希值得所有元素都保存在同一个桶中。如果允许重复关键字,所有具有相同关键字得元素也都会在同一个桶中。
    无序容器得性能依赖于哈希函数得质量和桶得数量和大小。

    理性情况下,每个特定的值都映射到唯一的桶,但是将不同的关键字映射到相同的桶也是允许的。
    桶的管理操作见表11.8

    无序容器对关键字的要求

    无序容器用==来比较,使用hash<key_tpye>类型的对象来生成每个元素的哈希值。
    标准库为内置类型都定义了hash,可以直接定义关键字是内置类型或string或智能指针的无序容器。
    不能直接定义自定义类型的无序容器,必须提供自己的hash模板。

    //定义哈希
    size_t hasher(const Sales_data &sd)
    {
        return hash<string>()(sd.isbn());
    }
    //定义比较
    bool eqOp(const Sales_data &lhs, const Sales_data &rhs)
    {
        return lhs.isbn() == rhs.isbn();
    }
    
    //定义一个unordered_multiset
    using SD_multiset = unordered_multiset<Sales_data, decltype(hasher)*, decltype(eqOp)*>;
    SD_multiset bookstore(42,hasher,eqOp);
    
    //如果类定义了==运算符,则可以只重载哈希函数
    unordered_set<Foo, decltype(FooHash)*> fooSet(10, FooHash);
    
  • 相关阅读:
    macbook 无声音解决方案
    webapck dev server代理请求 json截断问题
    百度卫星地图开启
    服务器 nginx配置 防止其他域名绑定自己的服务器
    记一次nginx php配置的心路历程
    遇到npm报错read ECONNRESET怎么办
    运行svn tortoiseSvn cleanup 命令失败的解决办法
    svn add 命令 递归目录下所有文件
    m4出现Please port gnulib freadahead.c to your platform! Look at the definition of fflush, fread, ungetc on your system, then report this to bug-gnulib."
    Ubuntu下安装GCC,mpc、mpfr、gmp
  • 原文地址:https://www.cnblogs.com/aqq2828/p/14204740.html
Copyright © 2020-2023  润新知