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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值