C++ Primer 中文版(第 5 版)练习解答合集
自己写的解答,如有错误之处,烦请在评论区指正!
1
map 是关联型容器,vector 是顺序性容器。
map 中的元素是键值对,并且自动按照键排序
2
list:不确定数据量大小,需要频繁插入删除,很少查找
vector:不需要频繁在中间插入删除,需要频繁随机访问
deque:只在头尾插入删除,需要频繁随机访问
map:需要存放两个集合之间的映射关系
set:需要存放不重复的元素,且要能快速查找是否已存在
3
#include <iostream>
#include <string>
#include <unordered_map>
using namespace std;
int main() {
string buffer;
unordered_map<string, int> word_cnt;
while (cin >> buffer)
++word_cnt[buffer];
for (auto item : word_cnt)
cout << item.first << " " << item.second << endl;
return 0;
}
4
#include <iostream>
#include <string>
#include <cctype>
#include <set>
#include <unordered_map>
using namespace std;
int main() {
string buffer;
// punct will be ignored at the end of the word
set<char> ignore{',', '.'};
unordered_map<string, int> word_cnt;
while (cin >> buffer) {
for (auto& ch : buffer)
ch = tolower(ch);
if (buffer.size() > 1 && ignore.find(buffer[buffer.size()-1]) != ignore.end())
buffer.erase(buffer.size()-1);
++word_cnt[buffer];
}
for (auto item : word_cnt)
cout << item.first << " " << item.second << endl;
return 0;
}
5
map 存放键值对,set 只存放键
需要存放对应关系的时候使用 map,只需要记录关键字就用 set
6
set 是关联型容器,list 是顺序容器
set 中的元素不能重复,list 可以
list 中元素的顺序可以在插入后自行调节,但是 set 一旦事先制定了排序规则,元素插入后就不能改变位置
7
#include <iostream>
#include <string>
#include <vector>
#include <map>
using namespace std;
int main() {
string family_name, kid_name;
map<string, vector<string>> family_kids;
while (cin >> family_name >> kid_name) {
family_kids[family_name].push_back(kid_name);
}
for (const auto& item : family_kids) {
cout << item.first << ":" << endl;
for (const auto& name : item.second) {
cout << name << " ";
}
cout << endl;
}
return 0;
}
8
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
int main() {
vector<string> vecSet;
string buffer;
while (cin >> buffer) {
if (find(vecSet.begin(), vecSet.end(), buffer) == vecSet.end())
vecSet.push_back(buffer);
}
for (auto str : vecSet)
cout << str << " ";
return 0;
}
set 会自动去掉重复的元素,并且寻找关键字的效率高,还会对关键字自动排序。
9
map<string, list<int>> word_pages;
10
vector 的迭代器之间支持小于运算符,而 list 的迭代器不支持。所以前者可以,后者不行(除非重载list 迭代器的小于运算符,或者给一个自定义的比较函数)。
11
multiset<Sales_data, bool (*)(const Sales_data& lhs, const Sales_data& rhs)> bookstore(cmp);
12
#include <iostream>
#include <vector>
#include <string>
#include <utility>
using namespace std;
int main() {
vector<pair<string, int>> vec;
int num;
string buffer;
while (cin >> buffer >> num) {
vec.emplace_back(make_pair(buffer, num));
}
for (auto p : vec)
cout << p.first << ": " << p.second << endl;
return 0;
}
13
#include <iostream>
#include <vector>
#include <string>
#include <utility>
using namespace std;
int main() {
vector<pair<string, int>> vec;
int num;
string buffer;
while (cin >> buffer >> num) {
// // version 1
// vec.emplace_back(make_pair(buffer, num));
// // version 2
// vec.emplace_back(pair<string, int>{buffer, num});
// version 3
vec.emplace_back(pair<string, int>(buffer, num));
}
for (auto p : vec)
cout << p.first << ": " << p.second << endl;
return 0;
}
14
#include <iostream>
#include <string>
#include <vector>
#include <map>
using namespace std;
int main() {
string family_name, kid_name, birth;
map<string, vector<pair<string, string>>> family_kids;
while (cin >> family_name >> kid_name >> birth) {
family_kids[family_name].push_back(make_pair(kid_name, birth));
}
for (const auto& item : family_kids) {
cout << item.first << ":" << endl;
for (const auto& p : item.second) {
cout << p.first << " " << p.second << endl;
}
cout << endl;
}
return 0;
}
15
对一个 int 到 vector<int> 的 map
mapped_type: vector<int>
key_type: const int
value_type: pair<const int, vector<int>>
16
map<int, int> int_ints;
int_ints[3] = 5;
auto it = int_ints.begin();
it->second = 10;
17
第 1、2 行非法。不能通过迭代器修改 set 中的关键字,所有迭代器都是 const_iterator
第 3、4 行合法。将 c 中所有 string 依次插入到 v 中
18
map<const string, size_t>::iterator
19
multiset<Sales_data, bool (*)(const Sales_data& lhs, const Sales_data& rhs)>::iterator it = bookstore.begin();
20
#include <iostream>
#include <string>
#include <cctype>
#include <set>
#include <unordered_map>
using namespace std;
int main() {
string buffer;
// punct will be ignored at the end of the word
set<char> ignore{',', '.'};
unordered_map<string, int> word_cnt;
while (cin >> buffer) {
for (auto& ch : buffer)
ch = tolower(ch);
if (buffer.size() > 1 && ignore.find(buffer[buffer.size()-1]) != ignore.end())
buffer.erase(buffer.size()-1);
auto ret = word_cnt.insert({buffer, 1});
if (!ret.second)
++ret.first->second;
}
for (auto item : word_cnt)
cout << item.first << " " << item.second << endl;
return 0;
}
21
如果插入失败,则把原本存在的这个 word 的值加 1
如果插入成功,则插入的这个 word 的值是 0,将其变成 1
总之,等价于原来的:
auto ret = word_cnt.insert({buffer, 1});
if (!ret.second)
++ret.first->second;
22
pair<map<string, vector<int>>::iterator, bool> insert(pair<const string, vector<int>>)
23
#include <iostream>
#include <string>
#include <vector>
#include <map>
using namespace std;
int main() {
string family_name, kid_name;
multimap<string, string> family_kids;
while (cin >> family_name >> kid_name) {
family_kids.insert({family_name, kid_name});
}
for (const auto& item : family_kids) {
cout << item.first << ": "
<< item.second << endl;
}
return 0;
}
24
新建一个 名为 m 的 map,是 int 类型到 int 类型的映射。
使用下标运算符寻找关键字为 0 的元素,没有找到,就将其值初始化,最后将其赋值为 1
25
对 vector 这样操作是非法的,因为 vector 现在是空的,不能用下标操作符去索引不存在的空间。
26
可以用该 map 的 key_type 类型来对 map 进行下标操作,返回的类型是 mapped_type
#include <iostream>
#include <string>
#include <map>
using namespace std;
int main() {
map<int, string> m;
m[6] = "Hello";
map<int, string>::key_type k = 6;
cout << m[k].substr(1, 2) << endl;
return 0;
}
27
需要明确知道关键字在关联容器中出现的次数(特别是对 multi 类型的关联容器)时,使用 count。如果只关心其是否出现,使用 find
28
map<string, vector<int>>::iterator it = m.find(target);
29
upper_bound 返回第一个安全的插入点,默认情况下是第一个大于给定关键字的位置
这种情况下,lower_bound 返回值和 upper_bound 一样
equal_range 返回一个 pair,其 first 成员和 second 成员同上
30
pos 是一个 pair 类型,其 first 成员的初始值是调用 lower_bound(search_item) 的返回值,即一个迭代器,指向第一个大于等于给定关键字的位置。而其 second 成员是调用 upper_bound(search_item) 的返回值
所以 pos.first 是一个用来遍历 lower_bound(search_item) 和 upper_bound(search_item) 范围内的迭代器,对其解引用后访问其 second 成员就得到了 map 中的值,即题目。
31
#include <iostream>
#include <string>
#include <map>
using namespace std;
int main() {
multimap<string, string> author_titles{
{"Jane Austen", "Pride and Prejudice"},
{"James Joyce", "Ulysses"},
};
auto it = author_titles.find("James Joyce");
if (it != author_titles.end())
author_titles.erase(it);
it = author_titles.find("XXX");
if (it != author_titles.end())
author_titles.erase(it);
for (auto item : author_titles)
cout << item.first << ": " << item.second << endl;
return 0;
}
32
#include <iostream>
#include <string>
#include <map>
using namespace std;
int main() {
multimap<string, string> author_titles{
{"Jane Austen", "Pride and Prejudice"},
{"James Joyce", "Ulysses"},
{"XXX", "aaaaaaa"},
{"XXX", "baaaa"},
{"Y", "aaa"},
{"Y", "z"},
{"XXX", "ba"},
};
auto it = author_titles.begin();
while (it != author_titles.end()) {
auto pos = author_titles.upper_bound(it->first);
cout << it->first << ":" << endl;
for (; it != pos; ++it)
cout << it->second << endl;
}
return 0;
}
33
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <unordered_map>
using namespace std;
int main() {
ifstream map_file("map_file.d");
ifstream words_file("words_file.d");
string input1, input2;
unordered_map<string, string> trans_map;
while (map_file >> input1 >> input2) {
trans_map[input1] = input2;
}
string line, word;
while (getline(words_file, line)) {
istringstream iss_line(line);
bool firstWord = true;
while (iss_line >> word) {
auto it = trans_map.find(word);
if (firstWord)
firstWord = false;
else
cout << " ";
cout << (it == trans_map.end() ? word : it->second);
}
cout << endl;
}
return 0;
}
34
如果换成下标运算符,那么查找 map 中不存在的字符串时,会自动值初始化一个键值对
35
如果输入中出现前后两个关键字相同,但是值不同的情况 ,行为就会出现差异。如果改成 insert 的写法,遇到下一个相同的关键字时,会插入失败。
结果上看,如果输入中有键值对 k, v1
, k, v2
…… k, vn
,用下标的写法最终 map 中是 k, vn
,但是用 insert 的写法 map 中是 k, v1
36
我的程序依然能运行。但是会忽略输入文件的最后一行,也不会对最后一个关键字作替换。
37
无序的优势在于,当维护元素有序的代价非常高昂,或者没有必要维护有序的时候,相对来说性能更好。
有序版本可以自动维护关键字有序。
38
用 unordered_map 替换 map 即可。