zoukankan      html  css  js  c++  java
  • c++ primer 第五版第十一章

    11.01 描述map和vector的不同。

    vector是顺序容器,只能存放单一类型的数据。

    map是关联的容器,存放一对key-value,这两个数据可以是不同类型。

    11.02 分别给出最适合使用list、vector、deque、map以及set的例子。

    1. list -- 需要在中间进行操作的情况。
    2. vector, 动态数组。
    3. deque -- 只需要在头尾进行操作的情况
    4. map -- 字典
    5. set -- 政府的黑名单

    11.03 编写你自己的单词计数程序。

    void test1103()
    {
        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;
        }
    }
    

    11.04 扩展你的程序,忽略大小写和标点。例如,“example.”、"example,"和"Example"应该递增相同的计数器。

    #include <iostream>
    #include <map>
    #include <algorithm>
    #include <string>
    
    //using namespace std;		不知原因,直接使用std,remove_if就编译不过。
    using std::remove_if;
    using std::string;
    using std::cin;
    using std::cout;
    using std::endl;
    using std::remove_if;
    using std::map;
    
    void test1103()
    {
        map<string, size_t> word_count;
        string word;
    
        while (cin >> word) {
            for (auto& ch : word) ch = tolower(ch);
            word.erase(remove_if(word.begin(), word.end(), ispunct), word.end());
            ++ word_count[word];
        }
    
        for (const auto& w : word_count) {
            cout << w.first << " occurs " << w.second << ((w.second > 1) ?  " times" : " time.")  << endl;
        }
    }
    

    11.05 解释map和set的区别。你如何选择使用哪个?

    map必须指明关键字类型和值类型

    set之存放关键字,没有值。使用哪种容器要根据使用场景进行选择。

    11.06 解释set和list的区别。你如何选择使用哪个?

    set是关联容器,进行查找、修改操作效率高

    list是顺序容器,插入删除等操作效率低。

    11.07 定义一个map,关键字是家庭的姓,值是一个vector,保存家中孩子们的名。编写代码,实现添加新的家庭以及向已有家庭中添加新的孩子。

    void test1107()
    {
        map<string, vector<string>> family;
        string family_name;
        vector<string> child_name;
        string name;
        int if_continue = 1;
        while (if_continue) {
            cout << "Please input the family name: ";
            cin >> family_name;
            cout << "Please input the child name: ";
            cin >> name;
            family[family_name].push_back(name);
            cout << "Continue?(1/0)";
            cin >> if_continue;
        }
    
        for (const auto& f : family) {
            cout << f.first << "		";
            for (const auto n : f.second) {
                cout << n << "	";
            }
            cout << endl;
        }
        cout << endl;
    }
    

    11.08 编写一个程序,在一个vector而不是一个set中保存不重复的单词。使用set的优点是什么?

    void test1108()
    {
        std::vector<std::string> exclude = { "aa", "bb", "cc", "dd", "ee", "ff" };
        for (std::string word; std::cout << "Enter word:
    ", std::cin >> word; )
        {
            auto is_excluded = std::binary_search(exclude.cbegin(), exclude.cend(), word);
            auto reply = is_excluded ? "excluded" : "not excluded";
            std::cout << reply << std::endl;
        }
    }
    

    set的查找速度更快。

    11.09 定义一个map,将单词与一个行号的list关联,list中保存的是单词所出现的行号。

    map<string, list<size_t>> m;
    

    11.10 可以定义一个vector::iterator到int的map吗?list::iterator到int的map呢?对于这两种情况,如果不能,解释为什么。

    map<vector<int>::iterator, int> m;
    map<list<int>::iterator, int> l;
    // 可以编译通过,但不能实际使用,因为iterator不能支持比较操作。
    

    11.11 不使用decltype重新定义bookstore。

    bool compareIsbn(const Sales_data& lhs, const Sales_data& rhs)
    {
        return lhs.isbn() > rhs.isbn();
    }
    
    void test1111()
    {
        using compare = bool(*)(const Sales_data&, const Sales_data&);
        multimap<Sales_data, compare> bookstore(compare);
    }
    // 用compare来初始化bookstore表示,当插入元素时,使用compare为元素排序
    

    11.12 编写程序,读入string和int的序列,将每个string和int存入一个pair中,pair保存在一个vector中。

    vectoe<pair<string, int>> pvec;
    

    11.13 在上一题的程序中,至少有三种创建pair的方法。编写此程序的三个版本,分别采用不同的方法创建pair。解释你认为哪种形式最易于编写和理解,为什么?

    void test1113()
    {
        vector<pair<string, int>> pvec;
        string str;
        int i;
        while (cin >> str >> i) {
        // approach 1
        //    pvec.push_back(make_pair(str, i));
        // approach 2
            pvec.push_back(pair<string, int>(str, i));
        // approach 3
        //    pvec.push_back({str, i});
        }
    
        for (auto i : pvec) {
            cout << i.first << "	" << i.second << endl;
        }
    }
    // 第一种形式更易理解。
    

    11.14 扩展11.2.1节练习中编写的孩子姓到名的map,添加一个pair的vector,保存孩子的名和生日。

    void test1114()
    {
        map<string, vector<pair<string, string>>> family;
        string family_name;
        vector<string> child_name;
        string name, birth;
        int if_continue = 1;
        while (if_continue) {
            cout << "Please input the family name: ";
            cin >> family_name;
            cout << "Please input the child name and birthday: ";
            cin >> name >> birth;
            family[family_name].push_back(make_pair(name, birth));
            cout << "Continue?(1/0)";
            cin >> if_continue;
        }
    
        for (const auto& f : family) {
            cout << f.first << "		";
            for (const auto n : f.second) {
                cout << n.first << "	" << n.second << "	";
            }
            cout << endl;
        }
        cout << endl;
    }
    

    11.15 对一个int到vector的map,其mapped_type、key_type和value_type分别是什么?

    对于map<int, vector> 类型

    mapped_type是vector类型

    key_type是int类型。

    value_type是pair<int, vector>类型

    11.16 使用一个map迭代器编写一个表达式,将一个值赋予一个元素。

    map<string, size_t> word;
    map<string, size_t>::iterator map_it = word.begin();
    map_it->second = 5;
    

    11.17 假定c是一个string的multiset,v是一个string的vector,解释下面的调用。指出每个调用是否合法:

    copy(v.begin(), v.end(), inserter(c, c.end()));	
    // 将v中的元素拷贝到c中。使用合法,可以使用inserter将关联容器当作一个目的位置。
    copy(v.begin(), v.end(), back_inserter(c));
    // 意义同上,但是不合法,因为multiset没有push_back方法,不能调用back_inserter
    copy(c.begin(), c.end(), inserter(v, v.end()));
    // 将c中的元素拷贝到v中,使用合法,vector可以使用inserter。
    copy(c.begin(), c.end(), back_inserter(v));
    // 意义同上,合法,vector有push_back方法,可以使用back_inserter.
    

    11.18 写出382页循环中map_it的类型,不要使用auto或decltype。

    map<string, size_t>::const_iterator map_it = word.cbegin();
    

    11.19 定义一个变量,通过11.2.2节中的名为bookstore的multiset调用begin()来初始化这个变量。写出变量的类型,不要使用auto或decltype。

    using compareType = bool (*)(const Sales_data &lhs, const Sales_data &rhs);
    std::multiset<Sales_data, compareType> bookstore(compareIsbn);
    std::multiset<Sales_data, compareType>::iterator c_it = bookstore.begin();
    

    11.20 重写11.1节练习的单词计数程序,使用insert代替下标操作。你认为哪个程序更容易编写和阅读?解释原因。

    void test1120()
    {
        map<string, size_t> word_count;
        string word;
    
        while (cin >> word) {
            auto ret = word_count.insert({word, 1});
            if (!ret.second) {
                ++ ret.first->second;
            }
        }
        for (const auto& w : word_count) {
            cout << w.first << " occurs " << w.second << ((w.second > 1) ?  " times" : " time.")  << endl;
        }
    }
    // 个人感觉下标操作更易编写和阅读。
    

    11.21 假定word_count是一个string到size_t的map,word是一个string,解释下面循环的作用。

    while (cin >> word)
     	++word_count.insert({ word, 0 }).first->second;
    

    向word_count中插入元素,如果word已存在,则解引用first迭代器,取值的部分++。

    如果word不存在,则插入(word,0),载解引用first迭代器,取值的部分++。

    while reading into word
        if word_count has key word:
            word_count[word] += 1
        else:
            word_count[word] = 0
            word_count[word] += 1
    

    11.22 给定一个map<string, vector>, 对此容器的插入一个元素的insert版本,写出其参数类型和

    返回类型。

    pair<string, vector<int>>		// 参数类型
    pair<map<string,vector<int>>::iterator, bool>		// 返回值类型
    

    11.23 11.2.1节练习中的map以孩子的姓为关键字,保存他们的名的vector,用multimap重写此map。

    void test1123()
    {
        multimap<string, string> family;
        string family_name;
        vector<string> child_name;
        string name;
        int if_continue = 1;
        while (if_continue) {
            cout << "Please input the family name: ";
            cin >> family_name;
            cout << "Please input the child name: ";
            cin >> name;
            family.insert({family_name, name});
            cout << "Continue?(1/0)";
            cin >> if_continue;
        }
    
        for (const auto& f : family) {
            cout << f.first << "	" << f.second << endl;
        }
    }
    

    11.24 下面的程序完成什么功能?

    map<int, int> m;
    m[0] = 1;
    

    在m中添加一个关键字0,将只“1”赋给它。

    11.25 对比下面程序与上一题程序

    vector<int> v;
    v[0] = 1;
    

    将“1” 存放在vector v的第一个位置。

    11.26 可以用什么类型来对一个map进行下标操作?下标运算符返回的类型是什么?请给出一个具体例子 -- 即,定义一个map,然后写出一个可以用来对map进行下标操作的类型以及下标运算符会返回的类型。

    void test1126()
    {
        map<int, string> m = {{1, "blue"}, {2, "yellow"}};
    
        using keyType = map<int, string>::key_type;
    
        cout << "map返回值的类型:" << typeid(keyType).name() << endl;
        cout << "map下标操作的类型:" << typeid(decltype(m[1])).name() << endl;
    }
    
    // 输出结果:map返回值的类型:i
    //          map下标操作的类型:NSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE
    // 不懂是什么意思~
    

    11.27 对于什么问题你会使用count来解决?什么时候你又会选择find呢?

    当只关心一个特定的元素是否在容器中,使用find。

    在允许重复关键字的容器中,如果要对某个特定的元素进行计数,则使用count。

    11.28 对于一个string到int的vector的map,定义并初始化一个变量来保存在其上调用find所返回的结果。

    void test1127()
    {
        map<string, vector<int>> vmap { { "apple",{ 1,2,3,4,5, } },{ "banana",{ 1,5,6,7,8 } }};
        map<string, vector<int>>::iterator iter;
    
        iter = vmap.find("apple");
    }
    

    11.29 如果给定的关键字不在容器中,upper_bound、lower_bound和equal_range分别会返回什么?

    如果给定的关键字不在容器中, 则upper_bound和lower_bound都返回关键字的第一个安全插入点 -- 不影响容器中元素顺序的插入位置。equal_range返回的两个迭代器都指向关键字可以插入的位置。

    11.30 对于本节最后一个程序中的输出表达式,解释运算对象pos.first->second的含义。

    pos.first->second意为给定关键字返回的第一个迭代器的value值。

    11.31 编写程序,定义一个作者及其作品的multimap。使用find在multimap中查找一个元素并用erase删除它。确保你的程序在元素不在map中时也能正常运行。

    void test1131()
    {
        multimap<string, string> auth_map = {
            {"John", "computer"},
            {"Joe", "science"},
            {"John", "art"},
            {"Angle", "design"}
        };
    
        string auth_name = "John";
        string work = "art";
      //  map<string, string>::iterator iter;
        auto iter = auth_map.find(auth_name);
        auto count = auth_map.count(auth_name);
    
        while (count) {
            if (iter->second == work) {
                auth_map.erase(iter);
                break;
            }
            else {
                ++ iter;
                -- count ;
            }
        }
    
        for (auto i : auth_map) {
            cout << i.first << " " << i.second << endl;
        }
    }
    

    11.32 使用上一题定义的multimap编写一个程序,按字典序打印作者列表和他们的作品。

    void test1132()
    {
        multimap<string, string> auth_map = {
            {"John", "computer"},
            {"Joe", "science"},
            {"John", "art"},
            {"Angle", "design"}
        };
    
        map <string, multiset<string>> order_auth;
        for (auto& auth : auth_map) {
            order_auth[auth.first].insert(auth.second);
        }
    
        for (const auto &author : order_auth) {
            std::cout << author.first << ": ";
            for (const auto &work : author.second)
                std::cout << work << " ";
            std::cout << std::endl;
        }
    }
    

    11.33 实现你自己版本的单词转换程序。

    map<string, string> buildMap(ifstream& map_file)
    {
        map<string, string> trans_map;
        string key, value;
    
        while (map_file >> key && getline(map_file, value)) {
            if (value.size() > 1) {
                trans_map[key] = value.substr(1);           // 去掉value前的空格
            }
            else {
                throw runtime_error("no rules for " + key);
            }
        }
        return trans_map;
    }
    
    const string& transform(const string& s, const map<string, string>& m)
    {
        auto map_iter = m.find(s);
    
        if (map_iter != m.end()) {
            return map_iter->second;
        }
        // if find the word in map, return the phrase. else return the origin word.
        else {
            return s;
        }
    }
    
    void wordTransform(ifstream& map_file, ifstream& input)
    {
        auto trans_map = buildMap(map_file);
        string text;
    
        while (getline(input, text)) {
            istringstream stream(text);
            string word;
            bool first_word = true;
    
            while (stream >> word) {
                if (first_word) {
                    first_word = false;
                }
                else {
                    cout << " ";
                }
                cout << transform(word, trans_map);
            }
            cout << endl;
        }
    }
    
    
    int main()
    {
        string transform_file = "C:/Users/tutu/Documents/code/cpp_primer/ch11/transform_file.txt";
        string input_file = "C:/Users/tutu/Documents/code/cpp_primer/ch11/input_file.txt";
    
        ifstream trans(transform_file);
        ifstream input (input_file);
    
        if (trans && input) {
            wordTransform (trans, input);
        }
        else {
            cout << "open file error!" << endl;
        }
        return 0;
    }
    

    11.34 如果你将transform函数中的find替换为下标运算符,会发生什么情况?

    若使用下标运算符,如果转换map中要查找的key不存在,则会直接插入一条新的数据,value为空。

    11.35 在buildMap中,如果进行如下改写,会有什么效果?

    trans_map[key] = value.substr(1);
    改为trans_map.insert({key, value.substr(1)})
    

    如果转换文件中有重复key,若使用下标进行插入,则value是最后文件中key对应的最后一个短语。而如果使用insert,则key对应的是第一个短语。

    11.36 我们的程序并没有检查输入文件的合法性,特别是,它假定转换规则文件中的规则都是有意义的。如果文件中的某一行包含一个关键字、一个空格,然后就结束了,会发生什么?预测程序的行为并进行验证,再与你的程序进行比较。

    如果文件中的某一行包含一个关键字、一个空格,然后就结束了,那程序会抛出runtime_error,提示no rules for k.

    11.37 一个无序容器与其有序版本相比有何优势?有序版本有何优势?

    有序容器的特点:

    1. 以顺序遍历元素,顺序可规定,默认情况下使用"<"进行排序。
    2. 主要使用排序vector,二叉搜索树实现。
    3. 查找元素操作的时间复杂度为O(logn)
    4. 插入、移动元素操作的时间复杂度更低,最大为O(logn)。

    无序容器的特点:

    1. 遍历元素没有特定的顺序。
    2. 如果hash策略良好,则搜索、插入、移动操作的时间复杂度都是常数。
    3. 主要使用哈希实现。

    11.38 用unordered_map重写单词计数程序和单词转换程序。

    void test1138()
    {
        unordered_map<string, size_t> word_count;
        string word;
    
        while (cin >> word) {
            ++ word_count[word];
        }
    
        for (const auto i : word_count) {
            cout << i.first << " occurs " << i.second << " times." << endl;
        }
    }
    
  • 相关阅读:
    哈工大中文篇章关系语料
    MongoDB学习笔记~关于官方驱动集成IQueryable之后的一些事
    MongoDB学习笔记~为IMongoRepository接口更新指定字段
    MongoDB学习笔记系列
    MongoDB学习笔记~为IMongoRepository接口添加了增删改方法,针对官方驱动
    MongoDB学习笔记~为IMongoRepository接口添加了排序和表达式树,针对官方驱动
    Android NDK入门实例 计算斐波那契数列二生成.so库文件
    Spring Autowire自动装配
    在gem5的full system下运行 alpha编译的测试程序 running gem5 on ubuntu in full system mode in alpha
    工厂三兄弟之抽象工厂模式(二)
  • 原文地址:https://www.cnblogs.com/songshuguiyu/p/9547602.html
Copyright © 2011-2022 走看看