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

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;
}
  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值