C++ Primer 第九章 顺序容器 2

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之后,其指向的是新元素之后的元素,而该元素是之前已经遍历过的,如果该元素是奇数,则会一直循环下去。
发布了38 篇原创文章 · 获赞 29 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/ruotianxia/article/details/102906274