11.1 描述map和vector的不同
map是关联式容器,vector是顺序容器
map中的元素是通过关键字来保存和访问的
vector中的元素是通过它们在容器中的位置来顺序保存和访问的
11.2 分别给出最适合使用list、vector、deque、map以及set的例子
对容器有大量删除和添加操作以及希望通过迭代器前后访问元素的时候可以用list
仅希望在尾部插入删除删除元素以及希望通过下标快速随机访问元素的时候适合用vector
希望在尾部和头部删除和添加元素以及希望通过下标快速随机访问元素的时候可以使用deque
希望通过键来查找对应的值得时候,例如通过学号查姓名,则适合于用map
只想知道一个值是否存在的时候,set是最有用的。
11.4 编写你自己的单词计数程序,忽略大小写和标点。例如"example."、“example,”、"Example"应该递增相同的计数器
void p11_4()
{
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)
{
//去除标点符号
word.erase(remove_if(word.begin(), word.end(), ispunct), word.end());
for (char& c : word)
c = tolower(c);
if (exclude.find(word) == exclude.end())
++word_count[word];
}
for (const pair<string, size_t>& w : word_count)
cout << w.first << " occurs " << w.second
<< ((w.second > 1) ? " times" : " time") << endl;
}
11.5 解释map和set的区别,你如何选择使用哪个
map是 字典,set是集合
map必须既指明关键字类型又指明值类型;而定义一个set时,只需要指明关键字类型。
需要存放一种映射关系的时候使用map
需要存放关键字的简单集合的时候就用set即可
11.6 解释set和list的区别。你如何选择使用哪个?
set是有序不重复集合,底层用红黑树实现,可以允许直接通过关键字进行查找
list允许关键字重复,但是只能通过前后移动进行顺序查找
11.7 定义一个map,关键字是家庭的姓,值是一个vector,保存家中孩子们的名。编写代码,实现添加新的家庭以及向家庭中添加新的孩子
void p11_7()
{
map<string, vector<string>> family;
string line;
//ln是姓,cn是名
//输入@q退出输入
for (string ln; cout << "Last name:\n", cin >> ln && ln != "@q";)
for (string cn; cout << "|-Children's names:\n", cin >> cn && cn != "@q";)
family[ln].push_back(cn);
for (const pair<string, vector<string>>& f : family)
{
cout << f.first << " has member:"<<endl;
for (const string& s : f.second)
cout << s << endl;
}
}
11.9 定义一个map,将单词与一个行号list关联,list中保存的是单词所出现的行号
map<string, list<size_t>> words;
11.10 可以定义一个vector<int>::iterator
到int
的map吗?list<int>::iterator
到int
的map呢?对于两种情况,如果不能,解释为什么
可以定义vector<int>::iteratror
到int
的map,但是不能定义list<int>::iterator
到int
的map。因为键必须要实现<
操作达到有序的特点,list的迭代器为双向迭代器不支持比较运算
11.11 不使用decltype重新定义bookstore
using Less = bool (*)(const Sales_data &,const Sales_data &);//Less是一个指针,指向一个返回值为bool,参数为两个常量引用的函数
multiset<Sales_data,Less> bookstore(less);
11.13 至少有三种创建pair的方法,解释你认为哪种形式最易于编写和理解,为什么?
采用 pair<string, int>(v1, v2)
方式来创建pair最易于编写和理解,因为明确的调用构造函数创建pair对象
11.14 扩展11.2.1节练习中编写孩子姓到名的map,添加一个pair的vector,保存孩子的名和生日
#include<iostream>
#include<vector>
#include<string>
#include<map>
using std::map;
using std::pair;
using std::cin;
using std::cout;
using std::endl;
using std::string;
using std::vector;
class Families
{
public:
using Child = pair<string, string>;
using Children = vector<Child>;
using Data = map<string, Children>;
void add(string const& last_name, string const& first_name, string birthday)
{
pair<string, string> child = make_pair(first_name, birthday);
_data[last_name].push_back(child);
}
void print() const
{
for (const pair<string, Children>& p : _data)
{
cout << p.first << endl;
for (const pair<string, string>& child : p.second)
cout << child.first << " " << child.second << endl;
}
}
private:
Data _data;
};
//测试代码
void p11_14()
{
Families fam;
string msg = "Please enter lastname, firstname and birthday: \n";
for (string ln, fn, b; cout << msg, cin >> ln >> fn >> b; fam.add(ln, fn, b));
fam.print();
}
11.15 对于一个int 到vector<int>
的map,其mapped_type、key_type和value_type分别是什么?
mapped_type是vector、key_type是int、value_type是pair<const int, vector>
11.17 假定c是一个string
的multiset
,v是一个string
的vector,解释下面的调用,指出每个调用是否合法。
multiset<string> c;
vector<string> v;
copy(v.begin(), v.end(), inserter(c, c.end()));
copy(v.begin(), v.end(), back_inserter(c)); //错误,因为multiset是没有push_back()函数的
copy(c.begin(), c.end(), inserter(v, v.end()));
copy(c.begin(), c.end(), back_inserter(v));
11.18 根据如下代码,写出map_it的类型
map<string, size_t> word_count;
auto map_it = word_count.cbegin();
map_it类型为map<string, size_t>::const_iterator
11.19
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
void test01()
{
map<string, size_t> word_count;
string word;
while (cin >> word)
{
//插入一个元素,key为word,value为1
//若word已在word_count中,insert什么也不做
pair<map<string, size_t>::iterator, bool> ret = word_count.insert({ word,1 });
if (!ret.second) //ret.second为false表明插入失败,即已经存在于map中
++ret.first->second;
}
//map的每个元素是pair
for (const pair<string, size_t>& e : word_count)
cout << e.first << " occurs " << e.second << " times" << endl;
}
我认为原来的版本更好,因为,新版本程序还必须搞明白insert返回值类型造成代码太乱,从而在元素已插入情况下只递增值。
11.21 解释下面循环作用
map<string, size_t> word_count;
string word;
while (cin >> word)
++word_count.insert({ word,0 }).first->second;
用map统计输入单词出现次数,具体步骤为:
-
先执行word_count.insert({word,0}) 插入pair,返回pair<map<string, size_t>::iterator, bool>,若插入成功,第二个成员返回true,否则返回false,第一个成员是一个指向插入元素的迭代器
-
然后.first取返回值的第一个成员(指向map元素(pair)迭代器)
-
然后再通过->second获取val
-
最后通过++,递增val的值
11.22 给定一个map<string, vector<int>>
,对此容器插入一个元素insert版本,写出其参数类型和返回类型
参数类型为pair<string, vector<int>>
返回类型pair<map<string, vector<int>>::iterator,bool>
11.26 可以用什么类型来对一个map进行下标操作?下标运算符返回的类型是什么?请给出一个具体的例子——即,定义一个map,然后写出一个可以用来对map进行下标操作的类型以及下标运算符将会返回的类型
可以用size_t类型对一个map进行下标操作,下标运算符返回的类型是mapped_type的左值,
11.27 对于什么问题你会用count来解决?什么时候你又会选择find呢?
使用可重复元素的关联式容器统计某一个元素出现的次数时候会使用count
对于不允许重复关键字的容器或只想知道一个给定关键字的元素是否存在于容器中时,会使用find
11.29 如果给定的关键字不在容器中,upper_bound、lower_bound和equal_range分别会返回什么
迭代器upper_bound和lower_bound都指向第一个排序上大于给定关键字的元素
equal_range返回一个迭代器pair,第一个和第二个迭代器都指向排序上第一个大于给定关键字的元素。
11.30 对于本节最后一个程序中的输出表达式,解释运算对象pos.first->second的含义。P391
pos.first获取第一个指向满足匹配关键字的元素(pair)的迭代器
->second获取匹配关键字元素的值(value)
11.32 定义一个作者及其作品的multimap,按字典序打印作者列表和他们的作品
void p11_32()
{
multimap<string, string> book;
book.insert({ "侯捷","深入理解C++对象模型" });
book.insert({ "侯捷","STL源码剖析" });
book.insert({ "stanely", "Effective C++" });
book.insert(std::make_pair("中本聪", "区块链"));
//将相同作者的书归结到一个multiset中
map<string, multiset<string>> authors; //用map保证作者唯一
for (const pair<string, string>& author : book)
authors[author.first].insert(author.second);
for (const pair<string, multiset<string>>& author : authors)
{
cout << author.first << " : " << endl;
for (const string& bn : author.second)
cout << bn << endl;
}
}
11.34 如果你将transform 函数中的find替换为下标运算符,会发生什么情况?
直接报错,因为下标和at操作只适用于非const的map和unordered_map
如果形参设置为非const,那么在 转换表m中根据键找不到对应转换关系的时候会返回空串
11.35 在buildMap中如果进行如下的改写,会有什么效果?
trans_map[key] = value.substr(1);
改为
trans_map.insert({key, value.substr(1)})
当遇到重复的键的时候,insert只会以第一次遇到键值对的为转换规则,而下标运算符是以最后一次键值对为准
11.36 如果我们的程序并没有检查输入文件的合法性。特别是,它假定转换规则文件中的规则都是有意义的。如果文件中的某一行包含一个关键字、一个空格,然后就结束了,会发生什么?预测程序的行为并进行验证,再与你的程序进行比较。
会抛出 runtime_error异常
11.37 一个无序容器与其有序版本相比有和优势?有序版本有何优势?
无序容器不需要进行排序,能够极大的减少时间开销,
有序版本的优势就是在于需要一些特定的处理需要关键字有序元素排列会比较好处理。