C ++ Primer(第五版)第十一章练习答案

    科技2024-07-09  73

    C ++ Primer(第五版)第十一章练习答案

    11.1 节练习练习 11.1练习 11.2练习 11.3练习 11.4 11.2.1 节练习练习 11.5练习 11.6练习 11.7练习 11.8 11.2.2 节练习练习 11.9练习 11.10练习 11.11 11.2.3 节练习练习 11.12练习 11.13练习 11.14 11.3.1 节练习练习 11.15练习 11.16练习 11.17练习 11.18练习 11.19 11.3.2 节练习练习 11.20练习 11.21练习 11.22练习 11.23 11.3.4 节练习练习 11.24练习 11.25练习 11.26 11.3.5 节练习练习 11.27练习 11.28练习 11.29练习 11.30练习 11.31练习 11.32 11.3.6 节练习练习 11.33练习 11.34练习 11.35练习11.36 11.4 节练习练习 11.37练习 11.38

    11.1 节练习

    练习 11.1

    描述 map 和 vector 的不同。

    map 是关联容器,称为关联数组,其元素是按关键字-值对来保存和访问的,它的关键字是元素的下标;

    vector 是顺序容器,其元素是按它们在容器中的位置来顺序保存和访问的,它的下标一定是数字。

    练习 11.2

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

    list:需要多次在除首位位置插入元素;

    vector:需要随机访问元素;

    deque:需要随机访问元素,同时需要在首位增删元素;

    map:保存具有映射的元素对,例如字典;

    set:保存需要用来判断的元素集合,元素本身是唯一的关键字。

    练习 11.3

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

    #include<iostream> #include<map> #include<string> using std::cin; using std::cout; using std::endl; using std::map; using std::string; int main() { map<string, size_t> word_count; string word; while (cin>>word) { ++word_count[word]; } for(const auto &w:word_count) { cout << w.first << " 出现了 " << w.second << " 次!" << endl; } return 0; }

    练习 11.4

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

    #include<iostream> #include<map> #include<set> #include<string> #include<algorithm> using namespace std; int main() { map<string, size_t> word_count; string word; while (cin>>word) { // 转换为小写 transform(word.begin(), word.end(), word.begin(), ::tolower); auto iter = word.end() - 1; if (ispunct(*iter)) { iter = word.erase(iter); } ++word_count[word]; } for(const auto &w:word_count) { cout << w.first << " 出现了 " << w.second << " 次!" << endl; } return 0; }

    11.2.1 节练习

    练习 11.5

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

    map包括关键字-值对; set只有关键字。

    练习 11.6

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

    set:是关联容器,查找较快,但不支持位置相关的操作; list:是顺序容器,查找关键字是和容器的大小有关系,支持位置相关的操作,功能更多。

    练习 11.7

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

    #include<iostream> #include<vector> #include<string> #include<map> using namespace std; int main() { map<string, vector<string>> family; string lName, fName; while (cin>> lName >> fName) { family[fName].push_back(lName); } for(const auto &f:family) { cout << f.first << " 家中有:" << endl; for(const auto &n:f.second) { cout << n << " "; } cout << endl; } return 0; }

    练习 11.8

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

    #include<iostream> #include<vector> #include<string> #include<algorithm> using namespace std; int main() { vector<string> sv; string str; while (cin>>str) { if (find(sv.begin(),sv.end(),str)==sv.end()) { sv.push_back(str); } } for(const auto &s : sv) { cout << s << " "; } cout << endl; return 0; }

    vector 需要判断输入是否重复;set 不需要。

    11.2.2 节练习

    练习 11.9

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

    map<string,list<size_t>> m

    练习 11.10

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

    前者可以,后者不行。因为 vector 的迭代器支持“严格弱序”操作,即“ < ”,而 list 的迭代器不支持。

    练习 11.11

    不使用 decltype 重新定义 bookstore。

    multiset<Sales_data, bool (*pf)(const Sales_data &lhs, const Sales_data &rhs)> bookstore(compareIsbn);

    11.2.3 节练习

    练习 11.12

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

    #include<iostream> #include<vector> #include<string> #include<utility> using namespace std; int main() { string str; int i; vector<pair<string, int>> pv; while (cin>>str>>i) { pv.push_back(pair<string, int>(str, i)); } for(const auto &p:pv) { cout << p.first << " " << p.second << endl; } return 0; }

    练习 11.13

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

    #include<iostream> #include<vector> #include<string> #include<utility> using namespace std; int main() { string str; int i; vector<pair<string, int>> pv; while (cin>>str>>i) { // 使用成员进行初始化 pv.push_back(pair<string, int>(str, i)); // 直接隐式调用默认构造函数转化为pair // pv.push_back({str, i}); // 调用make_pair函数进行构造 // pv.push_back(make_pair(str, i)); // 使用成员进行初始化 // pair<string, int> p(str, i); // pv.push_back(p); // 使用成员进行初始化 // pair<string, int> p = {str, i}; // pv.push_back(p); } for(const auto &p:pv) { cout << p.first << " " << p.second << endl; } return 0; }

    直接隐式构造最利于编写,指定成员类型并进行初始化最易于理解。

    练习 11.14

    扩展你在 11.2.1节练习(第378页)中编写的孩子姓到名的 map,添加一个 pair 的 vector,保存孩子的名和生日。

    #include<iostream> #include<vector> #include<string> #include<map> #include<sstream> #include<utility> using namespace std; int main() { //map<string, vector<string>> family; map<string, vector<pair<string, string>>> family; string lName, fName, birthday; while (cin>> lName >> fName>>birthday) { family[fName].push_back({lName, birthday}); } for(const auto &f:family) { cout << f.first << " 家中有:" << endl; for(const auto &n:f.second) { cout << n.first << " 生日是:" << n.second << endl; } cout << endl; } return 0; }

    11.3.1 节练习

    练习 11.15

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

    mapped_type:vector< int >

    key_type:int

    value_type:pair<const int, vector< int >>

    练习 11.16

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

    #include<iostream> #include<map> #include<string> using namespace std; int main() { map<int, string> sm{{1, "one"}}; map<int, string>::iterator iter = sm.begin(); iter->second = "two"; for(const auto &n:sm) { cout << n.first << " " << n.second << endl; } return 0; }

    练习 11.17

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

    copy(v.begin(), v.end(), inserter(c, c.end())); copy(v.begin(), v.end(), back_inserter(c)); copy(c.begin(), c.end(), inserter(v, v.end())); copy(c.begin(), c.end(), back_inserter(v));

    (1)合法。将 v 中的元素依次插入到 c 的尾部;

    (2)不合法。back_inserter 需要调用 push_back() 关联容器不支持;

    (3)和(4)合法。将 c 中的元素依次插入 v 的尾部;

    练习 11.18

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

    map<string, size_t>::iterator

    练习 11.19

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

    multiset<Sales_data, bool (*pf)(const Sales_data &lhs, const Sales_data &rhs)>:: iterator bookstore_iter = bookstore.begin();

    11.3.2 节练习

    练习 11.20

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

    #include<iostream> #include<map> #include<string> using namespace std; int main() { map<string, size_t> word_count; string word; while (cin>>word) { // ++word_count[word]; auto ret = word_count.insert({word, 1}); if (!ret.second) { ++(ret.first->second); } } for(const auto &w:word_count) { cout << w.first << " 出现了 " << w.second << " 次!" << endl; } return 0; }

    下标版本更容易编写和理解。insert 版本还要手动判断插入元素是否已经存在。

    练习 11.21

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

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

    insert 插入 word,并将它的计数值置为 0,返回一个 pair,pair 的 first 是插入元素的迭代器,迭代器指向一个 pair,pair 的 second 是这个 word 对应的计数值,即 0,最后将其自增结果为 1,表示插入元素并且计数为 1。

    练习 11.22

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

    参数类型:

    pair <string, vector< int >>

    返回类型:

    pair <map<string, vector< int >>::iterator, bool>

    练习 11.23

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

    #include<iostream> #include<vector> #include<string> #include<map> using namespace std; int main() { multimap<string, vector<string>> family; string lName, fName; while (cin>> lName >> fName) { vector<string> lNames; lNames.push_back(lName); family.insert({fName, lNames}); } for(const auto &f:family) { cout << f.first << " 家中有:" << endl; for(const auto &n:f.second) { cout << n << " "; } cout << endl; } return 0; }

    11.3.4 节练习

    练习 11.24

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

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

    检查 m 中是否有关键字为 0 的键-值对:如果有,将它的值赋值为1;如果没有,添加关键字 0,并赋值为1.

    练习 11.25

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

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

    没有初始化,不能自动插入元素。

    练习 11.26

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

    #include<iostream> #include<map> #include<string> using namespace std; int main() { map<int, string> ism{{0,"a"}}; int i = 0; string str = ism[i]; cout << str << endl; map<int, string>::key_type j = 0; map<int, string>::mapped_type mstr = ism[j]; cout << mstr << endl; return 0; }

    11.3.5 节练习

    练习 11.27

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

    要检查特定元素的关键字是否在容器中用 find; 如果是要统计指定元素的关键字的数量用 count。

    练习 11.28

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

    #include<iostream> #include<string> #include<vector> #include<map> using namespace std; int main() { map<string, vector<int>> s_ivm = {{"a", {1, 1, 1, 1}}}; map<string, vector<int>>::iterator iter = s_ivm.find("a"); cout << (iter->second)[0] << endl; return 0; }

    练习 11.29

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

    如果关键词不存在,upper_bound 和 lower_bound 的返回会指向同一个迭代器;equal_range 的返回迭代器 pair 中的两个迭代器会相等。

    练习 11.30

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

    pos 保存迭代器对,表示与关键字匹配的元素范围;

    pos.first 是匹配范围的开头迭代器,指向一个 mapped_value;

    pos.first->second 是解引用键值对中的值。

    练习 11.31

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

    #include<iostream> #include<map> #include<string> using namespace std; int main() { multimap<string, string> ssmm = {{"Mike", "aaa"}, {"Jone", "asas"}, {"Mike", "bbb"}}; ssmm.erase(ssmm.find("Jone")); for(const auto &m:ssmm) { cout << m.first << " " << m.second << endl; } return 0; }

    练习 11.32

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

    #include<iostream> #include<map> #include<string> #include<set> using namespace std; int main() { multimap<string, string> ssmm = {{"Mike", "bbb"}, {"Mike", "aaa"}, {"Jone", "asas"}}; map<string, set<string>> authors; for(const auto a:ssmm) { authors[a.first].insert(a.second); } for(const auto &m:authors) { cout << m.first << ":"; for(const auto & a:m.second) { cout << a << " "; } cout << endl; } return 0; }

    11.3.6 节练习

    练习 11.33

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

    #include<iostream> #include<string> #include<map> #include<fstream> #include<sstream> using namespace std; map<string, string> buildMap(ifstream &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("没有对 " + key + "的转换"); } } return trans_map; } const string & transform(const string &s, const map<string, string> &m) { auto map_it = m.find(s); if (map_it!=m.cend()) { return map_it->second; } else { return s; } } void word_transform(ifstream &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; } } int main() { ifstream word_file("word_transform_rule.txt"), input("word_text.txt"); word_transform(word_file, input); return 0; }

    练习 11.34

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

    遇到转换规则里没有的单词会插入到规则 map 中,并初始化为空。

    练习 11.35

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

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

    本程序中没有影响,如果关键字有多次出现,下标操作会将保留最后一次出现的值;而 insert 操作只会保留第一次的值。

    练习11.36

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

    if (value.size() > 1) { trans_map[key] = value.substr(1); } else { throw runtime_error("没有对 " + key + "的转换"); }

    只有一个空格,则 value.size() =1 会抛出异常!

    11.4 节练习

    练习 11.37

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

    无序容器能获得更好的平均性能;

    有序容器可以定义元素排序。

    练习 11.38

    用 unordered_map 重写单词计数程序(参见 11.1 节,第 375 页)和单词转换程序(参见 11.3.6 节,第 391 页)。

    #include<iostream> #include<unordered_map> #include<string> using namespace std; int main() { unordered_map<string, size_t> word_count; string word; while (cin>>word) { ++word_count[word]; } for(const auto &w:word_count) { cout << w.first << " " << w.second << endl; } return 0; } #include<iostream> #include<string> #include<unordered_map> #include<fstream> #include<sstream> using namespace std; unordered_map<string, string> buildunordered_map(ifstream &unordered_map_file) { unordered_map<string, string> trans_unordered_map; string key; string value; while (unordered_map_file>>key&&getline(unordered_map_file,value)) { if (value.size() > 1) { trans_unordered_map[key] = value.substr(1); } else { throw runtime_error("没有对 " + key + "的转换"); } } return trans_unordered_map; } const string & transform(const string &s, const unordered_map<string, string> &m) { auto unordered_map_it = m.find(s); if (unordered_map_it!=m.cend()) { return unordered_map_it->second; } else { return s; } } void word_transform(ifstream &unordered_map_file, ifstream &input) { auto trans_unordered_map = buildunordered_map(unordered_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_unordered_map); } cout << endl; } } int main() { ifstream word_file("word_transform_rule.txt"), input("word_text.txt"); word_transform(word_file, input); return 0; }
    Processed: 0.009, SQL: 8