C ++ Primer(第五版)第九章练习答案
9.1 节练习
练习 9.1
对于下面的程序任务,vector、deque 和 list 哪种容器最为适合?解释你的选择的理由。如果没有哪一种容器优于其他容器,也请解释理由。
(a)读取固定数量的单词,将它们按字典序插入到容器中。我们将在下一章中看到,关联容器更适合这个问题。
(b)读取未知数量的单词,总是将单词插入到末尾。删除操作在头部进行。
(c)从一个文件读取未知数量的整数。将这些数排序,然后将它们打印到标准输出。
(a)list,需要在中间插入数据,list最好;
(b)deque,需要在头部和尾部插入或删除元素,选 deque;
(c)vector。需要对读入容器中的数量进行排序,而 vector 支持随机访问,更适合用于此任务。
9.2 节练习
练习 9.2
定义一个 list 对象,其元素类型是int 的 deque。
list<deque<int>> lst;
9.2.1 节练习
练习 9.3
构成迭代器范围的迭代器有何限制?
begin 和 end 必须均指向同一个容器中的元素或尾元素之后的位置,且我们可以通过反复递增 begin 来到达 end ,即 end 不在 begin 之前。
练习 9.4
编写函数,接受一对指向 vector< int > 的迭代器和一个 int 值。在两个迭代器指定的范围中查找给定的值,返回一个布尔值来指出是否找到。
#include<iostream>
#include<vector>
using std::cout;
using std::endl;
using std::vector;
bool findVal(vector<int>::iterator b, vector<int>::iterator e, int i)
{
while (b!=e)
{
if (*b == i)
{
return true;
}
++b;
}
return false;
}
int main()
{
vector<int> iv = {0, 1, 2, 3, 4, 5};
int val = 6;
bool res = findVal(iv.begin(), iv.end(), val);
if (res)
{
cout << "找到目标值!" << endl;
}
else
{
cout << "没有找到目标值!" << endl;
}
return 0;
}
练习 9.5
重写上一题的函数,返回一个迭代器指向找到的元素。注意,程序必须处理未找到给定值的情况。
#include<iostream>
#include<vector>
using std::cout;
using std::endl;
using std::vector;
vector<int>::iterator findVal(vector<int>::iterator b, vector<int>::iterator e, int i)
{
while (b!=e)
{
if (*b == i)
{
return b;
}
++b;
}
return b;
}
int main()
{
vector<int> iv = {0, 1, 2, 3, 4, 5};
int val = 4;
// bool res = findVal(iv.begin(), iv.end(), val);
if (findVal(iv.begin(), iv.end(), val) != iv.end())
{
cout << "找到目标值!" << endl;
}
else
{
cout << "没有找到目标值!" << endl;
}
return 0;
}
练习 9.6
下面程序有何错误?你应该如何修改它?
list<int> lst1;
list<int>::iterator iter1 = lst1.begin(),iter2 = lst1.end();
while (iter1 < iter2) /* ... */
list 不是随机访问容器,因此迭代器没有重载小于运算符,故不能使用小于运算的形式。改为:
iter1 != iter2
9.2.2 节练习
练习 9.7
为了索引 int 的 vector 中的元素,应该使用什么类型?
vector<int>::size_type
练习 9.8
为了读取 string 的 list 中的元素,应该使用什么类型?如果写入 list,又应该使用什么类型?
list<string>::const_iterator //read
list<string>::iterator //write
9.2.3 节练习
练习 9.9
begin 和 cbegin 两个函数有什么不同?
begin 返回容器的 iterator 类型,当我们需要写访问时使用;
cbegin 返回容器的 const_iterator 类型,当我们不需要写访问时使用。
练习 9.10
下面4个对象分别是什么类型?
vector<int> v1;
const vector<int> v2;
auto it1 = v1.begin(), it2 = v2.begin();
auto it3 = v1.cbegin(), it4 = v2.cbegin();
it1 vector<int>::iterator
it2 vector<int>::const_iterator
it3 vector<int>::const_iterator
it4 vector<int>::const_iterator
9.2.4 节练习
练习 9.11
对 6 种创建和初始化 vector 对象的方法,每一种都给出一个实例。解释每个 vector 包含什么值。
vector<int> v1; // 默认初始化,空
vector<int> v2 = {0,1,2}; // 列表初始化,包含 0、1、2 这三个值
vector<int> v3(3); // 值初始化,包含 3 个 0
vector<int> v4(3,1); // 包含 3 个 1
vector<int> v5 = v4 // 拷贝,包含和 v4 内容相同
vector<int> v6(v5.begin(),v5.end()) // 初始化两个迭代器前后内容的拷贝
练习 9.12
对于接受一个容器创建其拷贝的构造函数,和接受两个迭代器创建拷贝的构造函数,解释它们的不同。
对于接受一个容器创建其拷贝的构造函数,要求两者的容器类型和元素类型都要相同;
而对于接受两个迭代器创建拷贝的构造函数,不要求容器类型相同,同时,新容器的元素也不必与原容器的元素类型相同,只要可以转换就行。
练习 9.13
如何从一个 list 初始化一个 vector?从一个 vector 又该如何创建?编写代码验证你的答案。
#include<iostream>
#include<vector>
#include<list>
using std::cout;
using std::endl;
using std::list;
using std::vector;
int main()
{
vector<int> v{0, 1, 2, 3, 4};
list<int> l{1, 2, 3};
vector<int> v1(l.begin(), l.end());
for(auto i:v1)
{
cout << i << " ";
}
cout << endl;
vector<int> v2 = v;
for(auto i:v2)
{
cout << i << " ";
}
cout << endl;
vector<double> v3(v.begin(), v.end());
for(auto i:v3)
{
cout << i << " ";
}
cout << endl;
return 0;
}
9.2.5 节练习
练习 9.14
编写程序,将一个 list 中的 char 指针(指向 C 风格字符串)元素赋值给一个 vector 中的 string。
#include<iostream>
#include<list>
#include<vector>
#include<string>
using std::cout;
using std::endl;
using std::list;
using std::string;
using std::vector;
int main()
{
list<const char *> lc = {"one", "tow", "three"};
vector<string> v;
v.assign(lc.begin(),lc.end());
for(auto i:v)
{
cout << i << " ";
}
cout << endl;
return 0;
}
9.2.7 节练习
练习 9.15
编写程序,判定两个 vector 是否相等。
#include<iostream>
#include<vector>
using std::cout;
using std::endl;
using std::vector;
int main()
{
vector<int> v1 = {0, 1, 2, 3};
vector<int> v2 = {0, 1, 2};
if (v1==v2)
{
cout << "相等" << endl;
}
else
{
cout << "不相等" << endl;
}
return 0;
}
练习 9.16
重写上一题的程序,比较一个 list 中的元素和一个 vector 中的元素。
#include<iostream>
#include<vector>
#include<list>
using std::cout;
using std::endl;
using std::list;
using std::vector;
int main()
{
vector<int> v1 = {0, 1, 2, 3};
list<int> l1 = {0, 1, 2, 3};
vector<int> v2(l1.begin(), l1.end());
if (v1 == v2)
{
cout << "相等" << endl;
}
else
{
cout << "不相等" << endl;
}
return 0;
}
练习 9.17
假定 c1 和 c2 是两个容器,下面的比较操作有何限制(如果有的话)?
if (c1 < c2)
(1)不能是无序容器;
(2)两容器类型相同,元素类型相同。
9.3.1 节练习
练习 9.18
编写程序,从标准输入读取 string 序列,存入一个 deque 中。编写一个循环,用迭代器打印 deque 中的元素。
#include<iostream>
#include<deque>
#include<string>
using std::cin;
using std::cout;
using std::deque;
using std::endl;
using std::string;
int main()
{
deque<string> dq;
string str;
while (cin >> str)
{
dq.push_back(str);
}
for (auto iter = dq.cbegin(); iter != dq.cend();++iter)
{
cout << *iter << " ";
}
cout << endl;
return 0;
}
练习 9.19
重写上题的程序,用 list 替代 deque。列出程序要做出哪些改变。
#include<iostream>
#include<list>
#include<string>
using std::cin;
using std::cout;
using std::list;
using std::endl;
using std::string;
int main()
{
list<string> lit;
string str;
while (cin >> str)
{
lit.push_back(str);
}
for (auto iter = lit.cbegin(); iter != lit.cend();++iter)
{
cout << *iter << " ";
}
cout << endl;
return 0;
}
练习 9.20
编写程序,从一个 list< int> 拷贝元素到两个 deque 中。值为偶数的所有元素都拷贝到一个 deque 中,而奇数值元素都拷贝到另一个 deque 中。
#include<iostream>
#include<list>
#include<deque>
using std::cout;
using std::deque;
using std::endl;
using std::list;
int main()
{
list<int> lst = {0, 1, 2, 3, 4, 5, 6};
deque<int> dq1, dq2;
// for (auto iter = lst.cbegin(); iter != lst.cend(); ++iter)
// {
// *iter % 2 ? dq1.push_back(*iter) : dq2.push_back(*iter);
// }
auto iter = lst.cbegin();
while (iter!=lst.cend())
{
*iter % 2 ? dq1.push_back(*iter) : dq2.push_back(*iter);
++iter;
}
for (const auto &s : dq1)
{
cout << s << " ";
}
cout << endl;
for (const auto &s : dq2)
{
cout << s << " ";
}
cout << endl;
return 0;
}
练习 9.21
如果我们将第 308 页中使用 insert 返回值将元素添加到 list 中的循环程序改写为将元素插入到 vector 中,分析循环将如何工作。
与添加到 list 中一样,循环依然等价于调用 push_front(但 vector 本身并不支持),将标准输入的字符依次插入 vector 的头部,但将非常耗时。
练习 9.22
假定 iv 是一个 int 的 vector,下面的程序存在什么错误?你将如何修改?
vector<int>::iterator iter = iv.begin(), mid = iv.begin() + iv.size() / 2;
while (iter != mid)
if (*iter == some_val)
iv.insert(iter, 2 * some_val);
(1)循环没有自增,iter 永远是指向开头元素;
(2)假设没有(1)的问题,在插入新元素后,容器的迭代器将会失效,iter 会指向新插入的元素,自增后又指向 some_val,将无限添加。
修改
vector<int>::iterator iter = iv.begin(), mid = iv.begin() + iv.size() / 2;
while (iter != mid)
{
if (*iter == some_val)
{// 插入后跳出循环
iv.insert(iter, 2 * some_val);
break;
}
++iter;
}
9.3.2 节练习
练习 9.23
在本节第一个程序(第 309 页)中,若 c.size() 为 1,则 val、val2、val3 和 val4 的值会是什么?
都是 c 容器中第一个元素的拷贝。
练习 9.24
编写程序,分别使用 at、下标运算符、front 和 begin 提取一个 vector 中的第一个元素。在一个空 vector 上测试你的程序。
#include<iostream>
#include<vector>
using std::cout;
using std::endl;
using std::vector;
int main()
{
vector<int> v1;
cout << v1.at(0) << endl;
cout << v1[0] << endl;
cout << v1.front() << endl;
cout << *v1.begin() << endl;
return 0;
}
9.3.3 节练习
练习 9.25
对于第 312 中删除一个范围内的元素的程序,如果 elem1 与 elem2 相等会发生什么?如果 elem2 是尾后迭代器,或者 elem1 和 elem2 皆为尾后迭代器,又会发生什么?
(1)相等则不删除元素;
(2)elem2 是尾后迭代器,则删除从 elem1 到最后的所有元素;
(3)都为尾后迭代器,则不删除元素。
练习 9.26
使用下面代码定义的 ia,将 ia 拷贝到一个 vector 和一个 list 中。使用单迭代器版本的 erase 从 list 中删除奇数元素,从 vector 中删除偶数元素。
int ia[] = { 0, 1, 1, 2, 3, 5, 8, 13, 21, 55, 89 };
#include<iostream>
#include<list>
#include<vector>
using std::cout;
using std::endl;
using std::list;
using std::vector;
int main()
{
int ia[] = {0, 1, 1, 2, 3, 5, 8, 13, 21, 55, 89};
list<int> lst;
vector<int> v;
for (auto i : ia)
{
lst.push_back(i);
v.push_back(i);
}
auto iterL = lst.begin();
while(iterL != lst.end())
{
if (*iterL % 2)
{
iterL = lst.erase(iterL);
}
else
{
++iterL;
}
}
auto iterV = v.begin();
while(iterV != v.end())
{
if (*iterV % 2 == 0)
{
iterV = v.erase(iterV);
}
else
{
++iterV;
}
}
for (const auto i : lst)
{
cout << i << " ";
}
cout << endl;
for (const auto i : v)
{
cout << i << " ";
}
cout << endl;
return 0;
}
9.3.4 节练习
练习 9.27
编写程序,查找并删除 forward_list 中的奇数元素。
#include<iostream>
#include<forward_list>
using std::cout;
using std::endl;
using std::forward_list;
int main()
{
forward_list<int> flst = {0, 1, 2, 3, 4, 5, 6, 7};
auto prev = flst.before_begin(), curr = flst.begin();
while(curr!=flst.end())
{
if (*curr % 2)
{
curr = flst.erase_after(prev);
}
else
{
++curr;
++prev;
}
}
for (const auto i : flst)
{
cout << i << " ";
}
cout << endl;
return 0;
}
练习 9.28
编写函数,接受一个 forward_list 和两个 string 共三个参数。函数应在链表中查找第一个 string,并将第二个 string 插入到紧接着第一个 string 之后的位置。若第一个 string 未在链表中,则将第二个 string 插入到链表末尾。
#include<iostream>
#include<string>
#include<forward_list>
using std::cout;
using std::endl;
using std::forward_list;
using std::string;
void insert_str(forward_list<string> &f, const string &s1, const string &s2)
{
auto prev = f.before_begin(), iter = f.begin();
while(iter!=f.end())
{
if (*iter==s1)
{
iter = f.insert_after(iter, s2);
return;
}
else
{
prev = iter;
++iter;
}
}
f.insert_after(prev, s2);
}
int main()
{
forward_list<string> flst = {"aaa", "bbb", "ddd"};
insert_str(flst, "bbb", "ccc");
for(const auto &i:flst)
{
cout << i << " ";
}
cout << endl;
insert_str(flst, "eee", "fff");
for(const auto &i:flst)
{
cout << i << " ";
}
cout << endl;
return 0;
}
9.3.5 节练习
练习 9.29
假定 vec 包含 25 个元素,那么 vec.resize(100) 会做什么?如果接下来调用 vec.resize(10) 会做什么?
(1)将值初始化的 75 个新元素添加到 vec 末尾;
(2)从 vec 的末尾删除 90 个元素。
练习 9.30
接受单个参数的 resize 版本对元素类型有什么限制(如果有的话)?
如果元素类型的类类型,则元素类型必须提供一个默认构造函数。
9.3.6 节练习
练习 9.31
第 316 页中删除偶数值元素并复制奇数值元素的程序不能用于 list 或 forward_list。为什么?修改程序,使之也能用于这些类型。
list 和 forward_list 不支持随机访问元素,迭代器不支持+2运算,因为链表的元素不是连续的随机内存,不能移动固定数字迭代器访问元素。
对于 list 和 forward_list,添加元素后,指向容器的迭代器仍有效;删除元素,指向容器其他位置的迭代器仍有效。
#include<iostream>
#include<list>
#include<forward_list>
using std::cout;
using std::endl;
using std::forward_list;
using std::list;
void copy_odd_lst(list<int> &l)
{
auto iter = l.begin();
while (iter != l.end())
{
if (*iter % 2)
{
l.insert(iter, *iter);
++iter;
}
else
{
iter = l.erase(iter);
}
}
for(const auto &i:l)
{
cout << i << " ";
}
cout << endl;
}
void copy_odd_flst(forward_list<int> &f)
{
auto iter = f.begin();
auto prev = f.before_begin();
while (iter != f.end())
{
if (*iter % 2)
{
f.insert_after(prev, *iter);
prev = iter;
++iter;
}
else
{
iter = f.erase_after(prev);
}
}
for(const auto &i:f)
{
cout << i << " ";
}
cout << endl;
}
int main()
{
list<int> lst = {0, 1, 2, 3};
copy_odd_lst(lst);
forward_list<int> flst = {4, 5, 6, 7};
copy_odd_flst(flst);
return 0;
}
练习 9.32
在第 316 页的程序中,像下面语句这样调用 insert 是否合法?如果不合法,为什么?
iter = vi.insert(iter, *iter++);
不合法。虽然上面语句可以正常运行,但是由于在 C++ 中并没有指定函数形参的入栈顺序,因此可能是先输入 iter 这个形参,或是先输入 *iter++ 这个形参。对于这两种情况而言,得到的结果将会不同。
练习 9.33
在本节最后一例中,如果不将 insert 的结果赋予 begin ,将会发生什么?编写程序,去掉此赋值语句,验证你的答案。
如果容器是 vector 或 string 以及 deque,迭代器将会失效,程序崩溃。如果是 list 将正常运行。
#include<iostream>
#include<vector>
#include<list>
using std::cout;
using std::endl;
using std::list;
using std::vector;
int main()
{
// vector<int> v = {0, 0, 0, 0, 0, 0};
// auto vBegin = v.begin();
// while (vBegin != v.end())
// {
// ++vBegin;
// // begin = v.insert(begin, 42);
// v.insert(vBegin, 42);
// ++vBegin;
// }
// for (const auto &i : v)
// {
// cout << i << " ";
// }
// cout << endl;
list<int> lst = {1, 1, 1, 1, 1, 1};
auto lsBegin = lst.begin();
while (lsBegin != lst.end())
{
++lsBegin;
// begin = v.insert(begin, 42);
lst.insert(lsBegin, 42);
++lsBegin;
}
for (const auto &i : lst)
{
cout << i << " ";
}
cout << endl;
return 0;
}
练习 9.34
假定 vi 是一个保存 int 的容器,其中有偶数值也要奇数值,分析下面循环的行为,然后编写程序验证你的分析是否正确。
iter = vi.begin();
while (iter != vi.end())
if (*iter % 2)
iter = vi.insert(iter, *iter);
++iter;
程序本意是希望找到容器中的奇数并复制。然而,当找到第一个奇数时,在这个数前插入这个数的复制值,并将 iter 指向这个新数,++iter 又将 iter 指向原奇数,无限循环。
9.4 节练习
练习 9.35
解释一个 vector 的 capacity 和 size 有何区别。
size 是指现在 vector 已经保存了的元素的数目;
capacity 是指在不分配新内存的情况下,vector 可以存放元素的最大数目。
练习 9.36
一个容器的 capacity 可能小于它的 size 吗?
不可能
练习 9.37
为什么 list 或 array 没有 capacity 成员函数?
list 不是连续的内存空间;array 是固定的 size 大小。
练习 9.38
编写程序,探究在你的标准实现中,vector 是如何增长的。
#include<iostream>
#include<vector>
using std::cin;
using std::cout;
using std::endl;
using std::vector;
int main()
{
vector<int> iv;
cout << iv.size() << " " << iv.capacity() << endl;
int a;
while (cin>>a)
{
iv.push_back(a);
cout << iv.size() << " " << iv.capacity() << endl;
}
return 0;
}
练习 9.39
解释下面程序片段做了什么:
vector<string> svec;
svec.reserve(1024);
string word;
while (cin >> word)
svec.push_back(word);
svec.resize(svec.size() + svec.size() / 2);
为 svec 申请 1024 个元素内存空间,输入的字符串存入 svec,最后将 vec 扩大元素个数的 0.5 被。
练习 9.40
如果上一题的程序读入了 256 个词,在 resize 之后容器的 capacity 可能是多少?如果读入了 512 个、1000 个、或 1048 个呢?
256、512 个:resize 之后没有超过预留空间 1024,则 capacity 没变化;
1000 个:resize 之后超过预留空间 1024 ,则 capacity 会增大不低于 1500;
1024 个:resize 之后超过预留空间 1024 ,则 capacity 会增大不低于 1536。
9.5.1 节练习
练习 9.41
编写程序,从一个 vector 初始化一个 string。
#include<iostream>
#include<vector>
#include<string>
using std::cout;
using std::endl;
using std::string;
using std::vector;
int main()
{
vector<char> cp{'H', 'i', ' ', 'C', '+', '+'};
string str(cp.begin(), cp.end());
for(const auto &s:str)
{
cout << s << " ";
}
cout << endl;
return 0;
}
练习 9.42
假定你希望每次读取一个字符存入一个 string 中,而且知道最少需要读取 100 个字符,应该如何提高程序的性能?
string s;
s.reserve(100);
9.5.2 节练习
练习 9.43
编写一个函数,接受三个 string 参数是 s、oldVal 和 newVal。使用迭代器及 insert 和 erase 函数将 s 中所有 oldVal 替换为 newVal。测试你的程序,用它替换通用的简写形式,如,将 “tho” 替换为 ”though“,将 “thru” 替换为 “through”。
#include<iostream>
#include<string>
using std::cout;
using std::endl;
using std::string;
void replace_str(string &s, const string &oldVal, const string &newVal)
{
auto iter = s.begin();
auto len = oldVal.size();
while (iter!=s.end())
{
if (oldVal == string(iter, iter + len))
{
iter = s.erase(iter, iter + len);
iter = s.insert(iter, newVal.begin(), newVal.end());
iter += len;
}
else
{
++iter;
}
}
}
int main()
{
string str("tho thru");
replace_str(str, "tho", "though");
replace_str(str, "thru", "thrugh");
cout << str << endl;
return 0;
}
练习 9.44
重写上一题的函数,这次使用一个下标和 replace。
#include<iostream>
#include<string>
using std::cout;
using std::endl;
using std::string;
void replace_str(string &s, const string &oldVal, const string &newVal)
{
string::size_type pos = 0;
auto len = oldVal.size();
while (pos != s.size())
{
if (oldVal == string(s, pos, len))
{
s.replace(pos, len, newVal);
}
++pos;
}
}
int main()
{
string str("tho thru");
replace_str(str, "tho", "though");
replace_str(str, "thru", "thrugh");
cout << str << endl;
return 0;
}
练习 9.45
编写一个函数,接受一个表示名字的 string 参数和两个分别表示前缀(如"Mr.“或"Ms.”)和后缀(如"Jr.“或"III”)的字符串。使用迭代器及 insert和 append 函数将前缀和后缀添加到给定的名字中,将生成的新 string 返回。
#include<iostream>
#include<string>
using std::cout;
using std::endl;
using std::string;
string name_add(string &n, const string &pre, const string &post)
{
n.insert(n.begin(), pre.begin(),pre.end());
n.append(post);
return n;
}
int main()
{
string name = "Johe";
cout << name_add(name, "Mr.", "III") << endl;
return 0;
}
练习 9.46
重写上一题的函数,这次使用位置和长度来管理 string,并只使用 insert。
#include<iostream>
#include<string>
using std::cout;
using std::endl;
using std::string;
string name_add(string &n, const string &pre, const string &post)
{
n.insert(0, pre);
n.insert(n.size(), post);
return n;
}
int main()
{
string name = "Johe";
cout << name_add(name, "Mr.", "III") << endl;
return 0;
}
9.5.3 节练习
练习 9.47
编写程序,首先查找 string"ab2c3d7R4E6" 中每个数字字符,然后查找其中每个字母字符。编写两个版本的程序,第一个要使用 find_first_of,第二个要使用 find_first_not_of。
#include<iostream>
#include<string>
using std::cout;
using std::endl;
using std::string;
void find_num1(const string &str, const string &num)
{
string::size_type pos = 0;
while ((pos = str.find_first_of(num, pos)) != string::npos)
{
cout << "found number at index: " << pos << ", element is " << str[pos] << endl;
++pos;
}
}
void find_num2(const string &str, const string &let)
{
string::size_type pos = 0;
while ((pos = str.find_first_not_of(let, pos)) != string::npos)
{
cout << "found number at index: " << pos << ", element is " << str[pos] << endl;
++pos;
}
}
void find_let1(const string &str, const string &let)
{
string::size_type pos = 0;
while ((pos = str.find_first_of(let, pos)) != string::npos)
{
cout << "found letter at index: " << pos << ", element is " << str[pos] << endl;
++pos;
}
}
void find_let2(const string &str, const string &num)
{
string::size_type pos = 0;
while ((pos = str.find_first_not_of(num, pos)) != string::npos)
{
cout << "found letter at index: " << pos << ", element is " << str[pos] << endl;
++pos;
}
}
int main()
{
string s("ab2c3d7R4E6");
string numbers{"123456789"};
string letters{"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"};
find_num1(s, numbers);
find_let1(s, letters);
cout << endl;
find_num2(s, letters);
find_let2(s, numbers);
return 0;
}
练习 9.48
假定 name 和 numbers 的定义如 325 页所示,numbers.find(name) 返回什么?
string::npos
练习 9.49
如果一个字母延伸到中线之上,如 d 或 f,则称其有上出头部分(ascender)。如果一个字母延伸到中线之下,如p或g,则称其有下出头部分(descender)。编写程序,读入一个单词文件,输出最长的既不包含上出头部分,也不包含下出头部分的单词。
#include<iostream>
#include<string>
#include<fstream>
using std::cerr;
using std::cout;
using std::endl;
using std::ifstream;
using std::string;
int main()
{
ifstream in("words.txt");
string word, result;
if (in)
{
while (in>>word)
{
if (word.find_first_of("bdfghijklpqty") == string::npos && word.size() > result.size())
{
result = word;
}
}
}else
{
cerr << "无法打开文件 words.txt" << endl;
return 0;
}
cout << "最长的既不包含上出头部分,也不包含下出头部分的单词是: " << result << endl;
return 0;
}
9.5.5 节练习
练习 9.50
编写程序处理一个 vector< string>,其元素都表示整型值。计算 vector 中所有元素之和。修改程序,使之计算表示浮点值的 string 之和。
#include<iostream>
#include<vector>
#include<string>
using std::cout;
using std::endl;
using std::string;
using std::vector;
int main()
{
vector<string> iv = {"0", "1", "2", "3", "4", "5", "6"};
int sum_int = 0;
for(const auto s:iv)
{
sum_int += stoi(s);
}
cout << sum_int << endl;
vector<string> dv(5, "4.56");
double sum_dou = 0.0;
for(const auto s:dv)
{
sum_dou += stod(s);
}
cout << sum_dou << endl;
return 0;
}
练习 9.51
设计一个类,它有三个 unsigned 成员,分别表示年、月和日。为其编写构造函数,接受一个表示日期的 string 参数。你的构造函数应该能处理不同的数据格式,如 January 1,1900、1/1/1990、Jan 1 1900 等。
#include<iostream>
#include<string>
#include<vector>
using std::cout;
using std::endl;
using std::string;
using std::vector;
class Date
{
private:
unsigned year;
unsigned month;
unsigned day;
public:
Date(const string &);
};
Date::Date(const string &s)
{
// 24/1/2019 格式
if (s.find("/") != string::npos)
{
string::size_type pre = s.find_first_of("/");
string::size_type post = s.find_last_of("/");
day = stoi(s.substr(0, pre));
month = stoi(s.substr(pre + 1, post - 1 - pre));
year = stoi(s.substr(post + 1));
}
// September 23,2018 格式
else if (s.find(",") != string::npos)
{
string::size_type blank = s.find_first_of(" ");
string::size_type comma = s.find_first_of(",");
day = stoi(s.substr(blank + 1, comma - 1 - blank));
year = stoi(s.substr(comma + 1));
vector<string> mons = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
for (size_t i = 0; i != s.size(); ++i)
{
if (s.find(mons[i]) != string::npos)
{
month = i + 1;
break;
}
}
}
// May 1 2020 格式
else
{
string::size_type preBlank = s.find_first_of(" ");
string::size_type postBlank = s.find_last_of(" ");
day = stoi(s.substr(preBlank + 1, postBlank - 1 - preBlank));
year = stoi(s.substr(postBlank + 1));
vector<string> mons = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
for (size_t i = 0; i != s.size(); ++i)
{
if (s.find(mons[i]) != string::npos)
{
month = i + 1;
break;
}
}
}
cout << year << " 年 " << month << " 月 " << day << " 日" << endl;
}
int main()
{
Date date1("24/1/2019");
Date date2("September 23,2018");
Date date3("May 1 2020");
return 0;
}
9.6 节练习
练习 9.52
使用 stack 处理括号化的表达式。当你看到一个左括号,将其记录下来。当你在一个左括号之后看到一个右括号,从 stack 中 pop 对象,直至遇到左括号,将左括号也一起弹出栈。然后将一个值(括号内的运算结果)push 到栈中,表示一个括号化的(子)表达式已经处理完毕,被其运算结果所替代。
傻了,这不是要做个计算器?网上找了一个
#include<iostream>
#include<queue>
#include<vector>
#include<cstring>
#include<stack>
using namespace std;
vector<string>prase(char *str) //分离每个符号
{
vector<string>tokens;
int len = strlen(str);
char *p = (char*)malloc((len + 1) * sizeof(char));
int i = 0, j = 0;
while (i < len) //去除空格
{
if (str[i] == ' ')
{
i++;
continue;
}
p[j++] = str[i++];
}
p[j] = '\0';
j = 0;
len = strlen(p);
while (j < len)
{
char temp[2] = { 0 }; //存储符号
string token;
switch (p[j])
{
case '+':
case '*':
case '/':
case '(':
case ')':
temp[0] = p[j];
temp[1] = '\0';
token = temp;
tokens.push_back(token);
break;
case '-':
if (p[j - 1] == ')' || isdigit(p[j - 1])) //不能使用--j,会死循环
{
temp[0] = p[j];
temp[1] = '\0';
token = temp;
tokens.push_back(token);
break;
}
else
{
temp[0] = '$';
temp[1] = '\0';
token = temp;
tokens.push_back(token);
break;
}
default:
i = j;
while (isdigit(p[i]))
i++;
char *operand = (char*)malloc((i - j + 1) * sizeof(char));
memset(operand, 0, i - j + 1);
memcpy(operand, p + j, i - j);
operand[i - j] = '\0';
token = operand;
tokens.push_back(token);
free(operand);
j = i - 1;
break;
}
j++;
}
free(p);
return tokens;
}
int priority(string opd)
{
int level;
if (opd == "$")
level = 3;
else if (opd == "*" || opd == "/")
level = 2;
else if (opd == "+" || opd == "-")
level = 1;
else if (opd == "(" || opd == ")")
level = 0;
return level;
}
void calculate(stack<int>& opdStack, string opt)
{
if (opt == "+")
{
int rOpd = opdStack.top();
opdStack.pop();
int lOpd = opdStack.top();
opdStack.pop();
int result = rOpd + lOpd;
opdStack.push(result);
}
else if (opt == "-")
{
int rOpd = opdStack.top();
opdStack.pop();
int lOpd = opdStack.top();
opdStack.pop();
int result = lOpd - rOpd;
opdStack.push(result);
}
else if (opt == "*")
{
int rOpd = opdStack.top();
opdStack.pop();
int lOpd = opdStack.top();
opdStack.pop();
int result = lOpd * rOpd;
opdStack.push(result);
}
else if (opt == "/")
{
int rOpd = opdStack.top();
opdStack.pop();
int lOpd = opdStack.top();
opdStack.pop();
int result = lOpd / rOpd;
opdStack.push(result);
}
else if (opt == "$")
{
int result = opdStack.top();
opdStack.pop();
result = -result;
opdStack.push(result);
}
}
int computationalExp(char *str)
{
vector<string>tokens = prase(str);
stack<int>opdStack;
stack<string>optStack;
for (int i = 0; i < tokens.size(); i++)
{
string token = tokens[i];
if (token == "+" || token == "-" || token == "*" || token == "/" || token == "$")
{
if (optStack.empty())
{
optStack.push(token);
}
else
{
int tokenPriority = priority(token);
string topOptStack = optStack.top();
int optPriority = priority(topOptStack);
if (tokenPriority > optPriority) //运算操作符优先于栈顶操作符,则入栈
{
optStack.push(token);
}
else
{//否则弹出操作符和操作数进行计算,直至当前操作符优先栈顶操作符时,入栈
while (tokenPriority <= optPriority)
{
calculate(opdStack, topOptStack);
optStack.pop();
if (optStack.size() > 0)
{
topOptStack = optStack.top();
optPriority = priority(topOptStack);
}
else
{
break;
}
}
optStack.push(token);
}
}
}
else if (token == "(")
{
optStack.push(token);
}
else if (token == ")")
{
while (optStack.top() != "(")
{
string topOpt = optStack.top();
calculate(opdStack, topOpt);
optStack.pop();
}
optStack.pop();//弹出"("
}
else
{
opdStack.push(stoi(token));//操作数入栈
}
}
while (optStack.size() != 0)
{
string topOpt = optStack.top();
calculate(opdStack, topOpt);
optStack.pop();
}
return opdStack.top();
}
int main()
{
char *str = "3+5-6*-9/3+(4+3*7)";
cout << computationalExp(str) << endl;
return 0;
}