9.18 编写程序,从标准输入读取string序列,存入一个deque 中,编写一个循环,用迭代器打印 deque 序列中的元素。
答:#include <iostream>
#include <deque>
using namespace std;
/*
deque 双端队列。支持快速随机访问,在头尾位置
插入或删除元素速度很快
*/
int main(int argc, char* argv[])
{
string word;
deque<string> sdeq;
while (cin >> word)
{
if (word == "n" || word == "N")
break;
sdeq.push_back(word);
}
auto begin = sdeq.cbegin();
auto end = sdeq.cend();
for (begin; begin != end; begin++)
{
cout << *begin << endl;
}
return 0;
}
9.19 重写上题程序,用list替代deque.列出程序需要做出哪些改变。
答:因为list在任何位置添加或删除元素都有很好的性能,而且遍历也性能很好,所以基本上与上体无差别。#include <iostream>
#include <deque>
using namespace std;
/*
deque 双端队列。支持快速随机访问,在头尾位置
插入或删除元素速度很快
*/
int main(int argc, char* argv[])
{
string word;
deque<string> sdeq;
while (cin >> word)
{
if (word == "n" || word == "N")
break;
sdeq.push_back(word);
}
auto begin = sdeq.cbegin();
auto end = sdeq.cend();
for (begin; begin != end; begin++)
{
cout << *begin << endl;
}
return 0;
}
9.20 编写程序,从一个list< int>拷贝元素到两个deque中,值为偶数的所有元素都拷贝到一个deque中,而奇数值元素都拷贝到另一个deque中。
答:#include <iostream>
#include <list>
#include <deque>
using namespace std;
int main(int argc, char* argv[])
{
list<int> ilist = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
deque<int> ideqEven, ideqOdd;
auto begin = ilist.begin();
auto end = ilist.end();
for (begin; begin != end; begin++)
{
if (*begin % 2 == 0)
{
ideqEven.push_back(*begin);
}
else
{
ideqOdd.push_back(*begin);
}
}
for (auto i : ideqEven)
{
cout << i << " ";
}
cout << endl;
for (auto i : ideqOdd)
{
cout << i << " ";
}
cout << endl;
return 0;
}
9.21 如果我们将第308页中使用insert返回值将元素添加到list中的循环程序改写为将元素插入到vector中,分析循环将如何工作。
答:因为 insert每次都返回首元素的迭代器,所以循环之后,得到的迭代器仍然是该容器的首元素迭代器,后续插入的元素都在之前的元素之前,所以插入的顺序是倒的。因为在vector的首元素之前插入元素,后面的所有元素的位置都会移动,因此其插入时间较长。#include <iostream>
#include <vector>
using namespace std;
int main(int argc, char* argv[])
{
vector<string> svec;
auto iter = svec.begin();
string word;
while (cin >> word)
{
iter = svec.insert(iter, word);
}
for (auto i : svec)
{
cout << i << endl;
}
return 0;
}
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);
答: 首先理解向一个vector、string、deque中插入元素会使得所有指向容器中的迭代器、引用和指针失效。题目中的程序中,iter没有进行递增操作,所以无法向mid推进。其次,即使加入了iter++, 由于iv中插入元素后,iter已经失效,所以iter++也不能将迭代器向前推进,修改后的程序如下:
#include <iostream>
#include <vector>
using namespace std;
int main(int argc, char* argv[])
{
vector<int> iv = { 1, 3, 5, 7 , 5, 5};
auto iter = iv.begin();
int orginalSize = iv.size(); // 原有尺寸
int newN = 0; // 新增元素
int val = 5;
// 因为要保证在mid出停止,所以要使得mid指向之前的元素,必须加上newN
while (iter != (iv.begin() + orginalSize / 2 + newN))
{
if (*iter == val)
{
iter = iv.insert(iter, 2 * val);
iter++;
iter++;
newN++;
}
else
{
iter++;
}
}
for (auto i : iv)
{
cout << i << endl;
}
return 0;
}
9.23 在本节第一个程序(第309页)中,若c.size()为1, 则val1, val2, val3, 和 val4的值会是什么?。
答:
309页代码
if (!c.empty())
{
auto val = *c.begin(), val2 = c.front();
auto last = c.end();
auto val3 = *(--last);
auto val4 = c.back();
}
若c.size() = 1, 说明c中是包含元素的,因此val1 = val2 =val3=val4.
9.24 编写程序,分别使用at、下标运算、front和begin提取一个vector中的第一个元素。在一个空vector上测试你的程序。
答: 在空vector中会报以下异常,Microsoft C++ 异常: std::out_of_range,
#include <iostream>
#include <vector>
using namespace std;
int main(int argc, char* argv[])
{
vector<int> ivec = { 1, 3, 5 };
cout << ivec.at(0) << endl;
cout << ivec[0] << endl;
cout << ivec.front() << endl;
cout << *ivec.begin() << endl;
return 0;
}
9.25 对于第132页中删除一个范围内的元素的程序,如果elem1和elem2相等会发生什么?如果elem2是尾后迭代器,或者elem1和elem2皆为尾后迭代器,又会发生什么?
答: 312页代码
elem1 = slist.erase(elem1, elem2);
如果两个皆为尾后迭代器,则返回尾后迭代器。如果elem1和elem2相等,会返回相等的迭代器。
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 <vector>
#include <list>
using namespace std;
template<class T> int get_array_length(T& arr)
{
return sizeof(arr) / sizeof(arr[0]);
}
int main(int argc, char* argv[])
{
int ia[] = { 0, 1, 1, 2, 3, 5, 8, 13, 21, 55, 89 };
int size = get_array_length(ia);
vector<int> ivec;
list<int> ilist;
for (int i = 0; i < size; i++)
{
ivec.push_back(ia[i]);
ilist.push_back(ia[i]);
}
{
list<int>::iterator it = ilist.begin();
while (it != ilist.end())
{
if (*it % 2)
it = ilist.erase(it);
else
++it;
}
for (auto i : ilist)
{
cout << i << endl;
}
}
{
vector<int>::iterator it = ivec.begin();
while (it != ivec.end())
{
if (!(*it % 2))
it = ivec.erase(it);
else
++it;
}
for (auto i : ivec)
{
cout << i << endl;
}
}
return 0;
}
9.27 编写程序,查找并删除forward_list< int>中的奇数元素。
答:
#include <iostream>
#include <forward_list>
using namespace std;
int main(int argc, char* argv[])
{
forward_list<int> flist = { 0, 1, 2,3,4,5,6,7,8,9 };
auto prev = flist.before_begin();
auto begin = flist.begin();
while (begin != flist.end())
{
if (*begin % 2)
{
begin = flist.erase_after(prev); // 删除 prev 之后的元素
// begin 返回删除元素之后的元素的迭代器
}
else
{
prev = begin; // 保存当前迭代器,因为erase_after删除的是当前迭代器之后的元素
begin++;
}
}
begin = flist.begin();
while (begin != flist.end())
{
cout << *begin << endl;
begin++;
}
return 0;
}
9.28 编写函数,接受一个forward_list和两个string共三个参数。函数应在量表中查找第一个string,并将第二个string,插入到紧接着第一个string之后的位置。若第一个string未在链表中,则将第二个string插入到链表末尾。
答:
// 这段代码是我理解错题意了,理解成两个都是搜索
#include <iostream>
#include <forward_list>
#include <string>
using namespace std;
int main(int argc, char* argv[])
{
forward_list<string> fstr = { "I", "Love", "You"};
string s1, s2;
s1 = "II";
s2 = "You";
auto begin = fstr.begin();
auto end = fstr.end();
forward_list<string>::iterator it1 = end, it2 = end;
while (begin != end)
{
if (*begin == s1 && it1 == end)
{
it1 = begin;
}
if (*begin == s2 && it2 == end)
{
it2 = begin;
}
if (it1 != end && it2 != end)
{
break;
}
begin++;
}
if (it1 != end && it2 != end)
{
fstr.insert_after(it1, *it2);
}
if (it1 == end && it2 != end)
{
auto pre_end = fstr.begin();
auto iter = pre_end;
while (iter != end) // 获取尾部元素位置
{
pre_end = iter;
iter++;
}
fstr.insert_after(pre_end, *it2);
}
for (auto i : fstr)
{
cout << i << endl;
}
return 0;
}
下面的是正确的代码
#include <iostream>
#include <forward_list>
using namespace std;
int main(int argc, char* argv[])
{
forward_list<string> fstr = { "A", "B", "C" };
string s1 = "A";
string s2 = "E";
auto pre_begin = fstr.before_begin();
auto begin = fstr.begin();
auto end = fstr.end();
bool inserted = false;
while (begin != end)
{
pre_begin = begin;
if (*begin == s1)
{
begin = fstr.insert_after(pre_begin, s2);
inserted = true;
}
if (inserted)
{
break;
}
begin++;
}
if (!inserted)
{
fstr.insert_after(pre_begin, s2);
}
for (auto i : fstr)
{
cout << i << " ";
}
cout << endl;
return 0;
}
9.31 第316页删除偶数值元素并复制奇数值元素的程序不能用于list或forward_list。为什么?修改程序,使之也能用于这些类型。
答:list和forward_list,不支持加减运算,因为链表中的元素并非在内存中连续存储,因此无法通过地址的加减在元素间远距离移动。因此,应多次调用++来实现与迭代器加法相同的效果。
#include <iostream>
#include <forward_list>
#include <list>
using namespace std;
void test_forward_list(forward_list<int>&);
void test_list(list<int>&);
int main(int argc, char* argv[])
{
forward_list<int> iflist;
list<int> ilist;
auto curr_iflist = iflist.before_begin();
for (int i = 0; i < 100; i++)
{
// Return a iterator pointing to the last inserted element.
curr_iflist = iflist.insert_after(curr_iflist, i);
ilist.push_back(i);
}
test_forward_list(iflist);
test_list(ilist);
for (auto begin = iflist.begin(); begin != iflist.end(); begin++)
{
cout << *begin << " ";
}
cout << endl;
for (auto begin = ilist.begin(); begin != ilist.end(); begin++)
{
cout << *begin << " ";
}
return 0;
}
void test_forward_list(forward_list<int> &iflist)
{
auto curr = iflist.begin();
auto prev = iflist.before_begin();
while (curr != iflist.end())
{
if (*curr % 2)
{
//Insert the element after a iterator, and
//return a iterator pointing to the last inserted element.
curr = iflist.insert_after(curr, *curr);
prev = curr;
curr++;
}
else
{
//Delete the element after a iterator, and
//return a iterator pointing to the element after the deleted element
curr = iflist.erase_after(prev);
}
}
}
void test_list(list<int>& ilist)
{
auto curr = ilist.begin();
while (curr != ilist.end())
{
if (*curr % 2)
{
//Insert the element before a iterator, and
//return a iterator pointing to the last inserted element.
curr = ilist.insert(curr, *curr);
curr++;
curr++;
}
else
{
//Delete the element pointing to a iterator, and
//return a iterator pointing to the element after the deleted element
curr = ilist.erase(curr);
}
}
}
9.32 在第316页的程序中,向下面语句这样调用insert是否合法?如果不合法,为什么?
iter = vi.insert(iter, *iter++)
。
答: 很多编译器对实参求值、像形参传递的处理顺序是由右至左的。这意味着,编译器在编译上述代码时,首先对*iter++求值,传递给insert的第二个形参,此时iter已经指向当前奇数元素的下一个元素,因此传递给insert的第一个参数的迭代器指向的位置是错误的,程序执行会发生混乱,最终崩溃。因此,若将代码改为iter = vi.insert(iter++, *iter);或是使用由左至右求值,传递参数的编译器,代码的运行结果是正确的。但是,这样的代码在逻辑上是毫无道理的。
9.33 在本节最后一个例子中,如果不将insert的结果赋予begin,将会发生什么?编写程序,去掉此赋值语句,验证你的答案。
答: 因为insert(p, t)的作用是在p之前插入t元素,并且返回最后一个插入元素的迭代器,因此若不将insert的的结果赋予begin, 当前的begin会失效,造成程序崩溃。对此程序,保存尾后迭代器和不向begin赋值两个错误存在其一,程序都会崩溃。
9.34 假定vi是一个保存int的容器, 其中有偶数也有奇数值,分析下面的循环的行为,然后编写程序验证你的分析是否正确。
iter = vi.begin();
while (iter != vi.end())
if (*iter % 2)
iter = vi.insert(iter, *iter);
++iter;
答: 从这段程序可以看到,这个程序的主要作用是将奇数元素赋值并插入当前奇数元素的前面。该段代码的第一个错误是没有使用花括号, ++iter变成了循环之外的一条语句。因为iter返回的是当前插入新元素的迭代器,因此其会继续将首元素插入到容器的首位置,会陷入死循环。即使将++iter放入到循环体之内,程序仍然是不对的。因为iter返回的是新插入的元素,++iter之后,其指向的是新元素之后的元素,而该元素是之前已经遍历过的,如果该元素是奇数,则会一直循环下去。