C++ Primer 第五版 课后章节练习答案 第十一章

编译软件为 vs2015。

第十一章

练习 11.1:

描述 map 和 vector 的不同。

map 是关键字—值 对的集合,通过关键字来查找值。

vector 是对象的集合,对象在容器中顺序保存和访问。

练习 11.2:

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

list:在容器中任意位置插入或删除元素

vector:保存一些重要的关联数据,按顺序查找元素

deque:先进先出

map:字典

set:查找不再序列中的元素

练习 11.3:

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

#include<map>
#include<iostream>
#include<string>
using namespace std;

int main()
{
	map<string, size_t> word_count;
	string word;
	while (cin >> word)
		++word_count[word];
	for (const auto &c : word_count)
		cout <<"( " <<c.first <<" )"<< " occurs " << c.second 
		<< ((c.second > 1) ? " times" : " time") << endl;
	system("pause");
	return 0;
}

练习 11.4:

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

#include<string>
#include<iostream>
#include<map>
#include<algorithm>
#include<cctype>
using namespace std;

int main()
{
	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;
	system("pause");
	return 0;
}

练习 11.5:

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

解答:

map:元素类型是关键字 — 值对

set:元素类型是关键字

练习 11.6:

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

解答:

set 中元素是唯一的和有序的,但 list 都不是。

练习 11.7:

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

解答:

#include<iostream>
#include<string>
#include<vector>
#include<map>
using namespace std;
int main()
{
	map<string, vector<string>> fam;
	string last_name, childs_name;
	cout << "Please input the family name: " << endl;
	cin >> last_name;
	cout << "Please input new childs' name: " << endl;
	while (cin >> childs_name)
	{
		fam[last_name].push_back(childs_name);
	}

	for (auto &s : fam)
	{
		cout << "====================" << endl;
		cout << "new family: " << endl;
		cout << s.first << endl;
		cout << "====================" << endl;
		cout << "new childs: " << endl;
		for (auto &c : s.second)
			cout << c << endl;
	}
	cout << "====================" << endl;
	system("pause");
	return 0;
}

 

练习 11.8:

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

解答:

#include<iostream>
#include<set>
#include<string>
#include<vector>
using namespace std;

int main()
{
	vector<string> vec = { "the", "red", "fox" };
	string word;
	while (cin >> word)
	{
		if (find(vec.cbegin(), vec.cend(), word) != vec.cend())
			cout << word << " is duplicate word " << endl;
		else
			vec.push_back(word);
	}
	for (const auto &c : vec)
		cout << c << " ";
	cout << endl;
	system("pause");
	return 0;
}

练习 11.9:

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

解答:

#include<iostream>
#include<map>
#include<string>
#include<list>
using namespace std;

int main()
{
	map<string, list<size_t>> map;
	return 0;
}

练习 11.10:

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

解答:

两种都可以。

#include<iostream>
#include<map>
#include<string>
#include<vector>
using namespace std;

int main()
{
	map<vector<int>::iterator, int> mv;
	map<list<int>::iterator, int> ml;

	return 0;
}

练习 11.11:

不使用 decltype 重新定义 bookstore。

解答:

#include<iostream>
#include<set>
#include"Sales_data.h"

using namespace std;

bool compareIsbn(const Sales_data &lhs, const Sales_data &rhs)
{
	return lhs.isbn < rhs.isbn;
}

int main()
{
	using compareType = bool(*) (const Sales_data &lhs, const Sales_data &rhs);
	//typedef bool(*compareType1)(const Sales_data &lhs, const Sales_data &rhs);
	multiset<Sales_data, compareType> bookstore(compareIsbn);
	return 0;
}

练习 11.12:

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

解答:

#include<iostream>
#include<utility>
#include<vector>
#include<string>

using namespace std;

int main()
{
	vector<pair<string, int>>vec;
	string str;
	int num;
	cout << "please input: " << endl;
	while (cin >> str >> num)
	{
		vec.push_back(pair<string, int>(str, num));
	}
	cout << "====================" << endl;
	for (const auto &c : vec)
		cout << c.first << " : " << c.second << endl;
	system("pause");
	return 0;
}

练习 11.13:

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

解答:

#include<utility>
#include<vector>
#include<string>
#include<iostream>

using namespace std;

int main()
{
	vector<pair<string, int>>vec;
	string str;
	int num;
	while (cin >> str >> num)
	{
		vec.push_back(make_pair(str, num));
		//vec.push_back({str, num})
		//vec.emplace_back(str, num); //!!! easiest way.
	}
	for (const auto &c : vec)
		cout << c.first << " : " << endl;

	return 0;
}

我个人认为用 make_pair 更易理解和编写程序。

练习 11.14:

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

解答:

#include<iostream>
#include<string>
#include<vector>
#include<map>
using namespace std;
int main()
{
	map<string, vector<pair<string, int>>> fam;
	string last_name, childs_name;
	int bir;
	cout << "Please input the family name: " << endl;
	cin >> last_name;
	cout << "Please input new childs' name ands birthday: " << endl;
	while (cin >> childs_name >> bir)
	{
		fam[last_name].push_back(make_pair(childs_name, bir));
	}

	for (auto &s : fam)
	{
		cout << "====================" << endl;
		cout << "new family: " << endl;
		cout << s.first << endl;
		cout << "====================" << endl;
		cout << "new childs: " << endl;
		for (auto &c : s.second)
			cout << c.first << " : " << c.second << endl;
	}
	cout << "====================" << endl;
	system("pause");
	return 0;
}

练习 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> ma;
	ma[3] = "Marry";
	std::ma<int, string>::iterator it = ma.begin();
	it->second = "Mike";
	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.begin()));

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

解答:

1. 合法。将序列 v 插入至 c 至其尾部。

2. 不合法。set 中没有 push_back。

3. 合法。

4. 合法。

练习 11.18:

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

解答:

std::map<string, size_t>::const_iterator 

练习 11.19:

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

解答:

using compareType = bool (*)(const Sale_data& lhs, const Sales_data & rhs);

std::multiset<Sales_data, compareType> bookstore(compareIsbn);

std::multiset<Sales_data,compareType>::iterator c_it = boolstore.begin();

练习 11.20:

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

解答:

#include<iostream>
#include<string>
#include<map>
using namespace std;
int main()
{
	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;
	system("pause");
	return 0;
}

练习 11.21:

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

while( cin >> word )

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

解答:

如果输入word,将 word 添加到 map 中,并首先将其计数器置为零,同时递增单词 word 对应的计数器。

练习11.22: 

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

解答:

插入的参数类型: pair<std::string, std::vector<int>>

返回类型: pair< map<string, vector<int>>::iterator, bool > 返回值是一个 pair,第一个为 map 类型的迭代器,第二个为 bool 。

练习 11.23:

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

解答:

#include<iostream>
#include<string>
#include<vector>
#include<map>
using namespace std;

int main()
{
	multimap<string, string> fam;
	string fam_name, chil_name;
	cout << "Please input the new family members: " << endl;
	while (cin >> chil_name >> fam_name)
	{
		fam.emplace(chil_name, fam_name);
	}
	cout << "====================" << endl;
	for (auto &s : fam)
	{
		cout << s.second << " " << s.first << endl;
	}
	cout << "====================" << endl;
	system("pause");
	return 0;
}

练习 11.24:

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

map< int, int > m;

m[0] = 1;

解答:

在 m 中寻找元素 “0” ,并将其计数器置为 1,若 m 中不存在 0 元素,则将 0 添加到 m 中,并将计数器置为 1。

练习 11.25:

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

vector<int> v;

v[0] = 1;

解答:

将容器 v 中的 0 号元素赋值为 1

练习 11.26:

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

解答:

#include<iostream>
#include<string>
#include<map>
using namespace std;

int main()
{
	map<string, size_t> word_count;
	word_count["the"] = 3;
	cout << word_count["the"] << endl;
	system("pause");
	return 0;
}

下标运算符返回的类型是 size_t。

练习 11.27:

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

解答:

对于允许重复元素出现的容器,需要对查找的关键字进行计数时,选择 count,而对于不允许重复元素出现的容器,或者不需要要对元素进行计数操作时,选择 find。

练习 11.28:

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

解答:

#include<iostream>
#include<string>
#include<vector>
#include<map>
using namespace std;

int main()
{
	map<string, vector<int>> m = { {"the", {2,4,6,8,12}}, {"quick",{ 1,3,5,7,9 }} };

	auto it = m.find("the");
	for (const auto &c : it->second)
		cout << c << " ";
	cout << endl;
	system("pause");
	return 0;
}

练习 11.29:

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

解答:

upper_bound 和 lower_bound 会返回相等的迭代器,并且都指向某个关键字的插入点,不影响容器中元素的顺序。

equal_bound 则返回一个迭代器 pair,pair 的两个成员均等于 c.end()。

练习 11.30:

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

解答:

pos:pair 迭代器。

pos.first:pair 的第一个成员,指向一个与关键字匹配的元素的迭代器。

pos.first->second: 解引用此迭代器,获取指向 pair 中的第一个与关键字匹配的元素的 second 成员。

练习 11.31:

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

解答:

#include<iostream>
#include<string>
#include<map>
using namespace std;

int main()
{
	multimap<string, string> m;
	m = { {"Anna", "anna"}, {"Marry", "marry"}, {"Anna", "anna2"}, {"John", "john"} };
	string author, word;
	cout << "Please input the author's name you want to delete: " << endl;
	cin >> author >> word;
	for (auto iter = m.find(author); iter != m.cend() && iter->first == author;)
	{
		if (iter->second == word)
			iter = m.erase(iter);
		else
			++iter;
	}
	cout << "====================" << endl;
	for (auto &c : m)
		cout << c.first << " " << c.second << endl;
	cout << endl;
	system("pause");
	return 0;
}

练习 11.32:

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

解答:

#include<iostream>
#include<string>
#include<map>
#include<set>
using namespace std;

int main()
{
	multimap<string, string> m;
	m = { {"Anna", "Fly"}, {"Marry", "Red"}, {"John", "Fox"}, {"Anna", "Jump"}, {"John", "Bird"},
		  {"John", "Bad Man"}, {"King", "Steve"}, {"Bob", "Brother"} };
	map<string, multiset<string>> new_m;
	for (auto &c : m)
		new_m[c.first].insert(c.second);
	cout << "==============================" << endl;
	for (auto &s : new_m)
	{
		cout << s.first << ":";
		for (auto &ss : s.second)
			cout << " " << ss << ",";
		cout << endl;
	}
	cout << "==============================" << endl;
	system("pause");
	return 0;
}

练习 11.33:

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

解答:

#include<iostream>
#include<sstream>
#include<fstream>
#include<string>
#include<map>
using namespace std;

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;                  // 否则直接返回该单词
}

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("no rule for " + key);
	}
	return trans_map;
}


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 rule("...\\rule.txt"), 
		text("...text.txt");
	word_transform(rule, text);
	system("pause");
	return 0;
}

练习 11.34:

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

解答:

下标运算符会当单词不在转换规则中时,将会执行将此单词添加到转换规则中的操作,但是 m 已经声明为了 const 型,不能写入新的值,因此会发生错误。

练习 11.35:

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

trans_map[key] = value.substr(1);

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

解答:

改为 insert 后,如果一个单词重复出现多次,循环只会将最先一个对应短语存入 trans_map,即重复单词不会重复插入。

练习 11.36:

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

解答:

程序会出现异常。

修改如下:

#include<iostream>
#include<sstream>
#include<fstream>
#include<string>
#include<map>
using namespace std;

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;                  // 否则直接返回该单词
}

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).substr(0, value.find_last_not_of(' '));  //跳过前导空格
	}
	return trans_map;
}


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 rule("...rule2.txt"), 
		text("...text.txt");
	if (rule && text)
		word_transform(rule, text);
	else
		cout << "Not found the right files!" << endl;
	system("pause");
	return 0;
}

练习 11.37:

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

解答:

无序容器的优势:在关键字类型的元素没有明显的序关系的情况下,以及在某些应用中,维护元素的序代价非常高昂,此时使用无序容器更好。

有序容器的优势:迭代器可以访问元素,同时还可以定义关键字类型为自定义类类型的容器,但是无序容器只能定义关键字是内置类型(包括指针类型,string,智能指针类型的无序容器)。

练习 11.38:

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

解答:

单词计数程序:

#include<iostream>
#include<string>
#include<unordered_map>
using namespace std;

int main()
{
	unordered_map<string, size_t> word_count;
	string word;
	cout << "Please input: " << endl;
	while (cin >> word)
		++word_count[word];
	for (const auto &w : word_count)
		cout << w.first << " occurs " << w.second << (w.second > 1 ? " times" : " time") << endl;
	system("pause");
	return 0;
}

 

与之前使用 map 编写的相同的单词计数程序相比,发现使用无序容器时,其不会按照字典顺序输出。

单词转换程序:

#include<iostream>
#include<sstream>
#include<fstream>
#include<string>
#include<map>
#include<unordered_map>
using namespace std;

const string & transform(const string &s, const unordered_map<string, string> &m)
{
	auto map_it = m.find(s);
	if (map_it != m.cend())        // 如果单词在转换规则中
		return map_it->second;     // 使用短语替换
	else
		return s;                  // 否则直接返回该单词
}

unordered_map<string, string> buildmap(ifstream &map_file)
{
	unordered_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).substr(0, value.find_last_not_of(' '));  //跳过前导空格
	}
	return trans_map;
}

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 rule("...rule.txt"),
		text("...text.txt");
	if (rule && text)
		word_transform(rule, text);
	else
		cout << "Not found the right files!" << endl;
	system("pause");
	return 0;
}

 

 

 

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值