C++ Primer 第十章 10.2 初识泛型算法 练习和总结

10.2 初识泛型算法

C++标准库提供的算法有100多个,一下子记不住这么多,我们可以辅助记忆。

大部分的算法接收两个参数表示可以操作的范围,这个范围叫做输入范围。
(其实我觉得也可以叫做迭代器范围)

泛型算法根据对元素的操纵方式分为
是否读取元素、改变元素、或是否重排元素。根据这些可以对算法进行大致划分。

10.2.1 只读算法

accumulate算法
accumulate(iter1,iter2,初始值)

该算法对输入范围内的元素进行累加操作,其中初始值类型和传入的容器元素类型可以不一致,只要容器类型可以转化为初始值类型。

这句话要记住,accumulate算法返回值类型以及在算法中使用的运算符都是使用初始值类型。

vector<int> vec = {1,2,3,2134,3214,123,4,123,321,312,3,123,12,3,12,31,23,12};
	long value = 0;
	//元素类型和初始值类型是一样的,或者说元素类型可以转化为初始值类型
	//泛型算法返回值类型的是初始值类型
	//而且在该算法中,使用的是初始值类型的运算符
	auto sum = std::accumulate(vec.begin(),vec.end(),value);

对于accumulate的操作,这里我的理解是元素类型先转化为初始值类型,再进行累加 ,即使用+运算符。

对于一下的代码:

vector<string> str_vec = {"123","321","233","xx","gg"};
auto str_sum = std::accumulate(str_vec.begin(), str_vec.end(),"");

先把string类型转化为const char* ,然后使用const char* 的+运算符。

但是const char* 没有+运算符,所以程序编译报错。

equal算法
equal(容器1输入范围1,容器1输入范围2,容器2输入范围1)

equal判断两个输入范围内的元素是否相等,因为传入的是迭代器,所以两个容器类型可以不一致,两个容器内的元素也可以不一致,只要可以执行==运算符即可

这类算法接收三个参数,前两个参数输入容器1的输入范围,后一个参数输入容器2的首元素。因为容器2只输入2只输入了首元素,所以这类算法都假定容器2中元素的个数大于等于容器1的元素个数

而另一些方法可以接收四个参数,这也可以输入两个完整的输入范围。

练习

10.3


vector<int> vec = {1,2,3,2134,3214,123,4,123,321,312,3,123,12,3,12,31,23,12};
long value = 0;
auto sum = std::accumulate(vec.begin(),vec.end(),value);

sum是double类型

10.4

vector<double> vec = {1,2,3,2134,3214,123,4,123,321,312,3,123,12,3,12,31,23,12};
int value = 0;
auto sum = std::accumulate(vec.begin(),vec.end(),value);
cout<<sum<<endl;

因为初始值类型为int型,所以double会转为int型,这可能导致精度的丢失

10.5

元素类型为const char*类型,所以equal是比较指针是否相等。

但是我记得,如果const char* 所赋值的字符串字面值是一样的,那么他们的指针也是一样的。

所以这同样可以比较是否相等。

vector<const char*> str_vec= {"hello","world"};
	deque<const char*> str_vec_2 = {"hello","world"};
	auto is_equal = std::equal(str_vec.begin(), str_vec.end(),str_vec_2.begin());
	cout << is_equal << endl;

该代码is_euqal为true。

写容器元素的算法

算法在对容器中的元素进行操作时,并不会改变容器的大小。
并不会改变容器的大小!!!
并不会改变容器的大小!!!
并不会改变容器的大小!!!
并不会改变容器的大小!!!

这里的大小指的是size(),而不是capacity()

写入算法

fill(序列输入范围1,序列输入范围2,初始值);
fill_n(序列输入范围1,计数器,初始值)

注意这些算法都不会改变容器的大小,所以对空容器使用fill和fill_n都是错误的。算法也不会检查操作是否符合条件。

back_inserter迭代器

算法不能改变容器的大小,这限制了容器的一些操作,C++提供了back_inserter迭代器,用于向容器中插入元素,我们可以在算法中使用该迭代器,向容器中插入元素。

对该迭代器进行赋值操作时,一个与赋值符号右边相等的元素会被添加进容器中。

在进行赋值时,赋值运算符将调用push_back()将给定值添加到容器中。

vector<int> vec;
	auto it = std::back_inserter(vec);
	*it = 42;
	cout << vec.size() << endl;

所以使用back_inserter和fill_n,我们就可以为空容器赋值。

vector<int> vec;
	auto it = std::back_inserter(vec);
	std::fill_n(it,10,0);
	cout << vec.size() << endl;

这里只是了解,对于back_iterator的具体内容,后续的章节会讲到。

copy算法

copy算法将输入序列范围内的值,拷贝的目标序列中.

这要求目标序列的大小至少要等于输入范围的大小。

vector<int> vec = {1,2,3,4,5,6,7,8,9,0};
	deque<double> dq(10);
	std::copy(vec.begin(),vec.end(),dq.begin());
	for (const auto& item:dq) {
		cout << item << endl;
	}

同样,容器类型和容器内元素类型都可以不一样,只要可以调用赋值运算符就可以。

copy将返回目标序列递增之后,下一个目标迭代器的值,大白话就是copy了n个元素,copy将返回dq.beign()+n这个元素的迭代器。

有很多算法提供了自己的copy版本, 即算法得到的结果不在原来的目标序列上表现,而是在新的序列上保存这些结果。

比如

replace(序列输入范围1,序列输入范围2,原始值,替换值)
它的copy版本
replace(序列输入范围1,序列输入范围1,保存结果的序列的迭代器,原始值,目标值)

它将替换值之后的序列保存在第三个参数指明的序列中。

以下的代码将替换后的结果,保存在deque上面。

vector<int> vec = { 1,2,3,4,5,6,7,8,9,0 };
deque<double> dq(10);
std::replace_copy(vec.begin(), vec.end(),dq.begin(), 0, 233);

练习

10.6

vector<int> int_vec(10,1);
	std::fill_n(int_vec.begin(), int_vec.size(), 0);
	for (const auto& item:int_vec) {
		cout << item << endl;
	}

10.7
a.使用copy时,目标序列的size()至少要等于输入序列的size()

b.算法fill_n并不会改变容器本身的大小,而vec的size()为0,所以无法写入10个0.

10.8
不会,因为改变容器大小的并不是算法,而是迭代器。

10.2.3 重排容器元素的算法

有些算法可以改变容器中元素的位置。
比如

sort(输入范围1,输入范围2)

sort可以将输入范围内的元素进行排序

unique()可以消除重复的元素

unique可以去除连续出现的抽工夫元素,所谓的连续出现的重复元素,即1,2,2,2,3。这里的2是重复的,所以使用unique可以“消除”多余的2,但是如果序列为1,2,3,2,3,2,3.序列中元素虽然有重复,但是不是连续出现,所以unique并不能消除这种重复出现的元素。

unique()将返回最后一个非重复元素的下一个元素的迭代器。这么做是有意义的,因为unique(),并不会改变容器中元素的大小,它只是把不重复的元素都放在了前面,容器的大小是没有变化的

返回这个迭代器之后,我们就可以调用容器本身的删除函数,将多余的重复元素删除。

这也,我们可以通过组合算法,来完成一些特定的任务。

比如练习10.9

练习


//排序并消除重复的元素
void elimDups(vector<string>& word) {

	std::sort(word.begin(),word.end());
	for (const auto& item:word) {
		cout << item << std::ends;
	}
	cout << endl;

	auto end_iter =   std::unique(word.begin(),word.end());
	cout << word.size() << endl;
	for (const auto& item : word) {
		cout << item << std::ends;
	}
	cout << endl;
	word.erase(end_iter, word.end());
	cout << word.size() << endl;
	for (const auto& item : word) {
		cout << item << std::ends;
	}
	cout << endl;
}

测试用例:
vector<string> vec = {"a","c","b","e","x","a","z","q","c"};
	elimDups(vec);

输出结果为
在这里插入图片描述
可以看到unique并没有改变容器的大小

10.10

之前学习过,改变容器的大小可能会导致迭代器,指针,或者指向元素的引用失效。

如果算法可以改变迭代器的大小,那么就意味着算法需要为了让迭代器有效,而做出更多和算法无关的操作。
这让代码变的很冗余。

同时算法改变了容器的大小,也让程序变得更加复杂,不能够让程序员所控制。

总结一下目前算法序列中元素的操纵方式
1.只读取元素(count(),accumulate())
2.修改元素(fill(),fill_n())
3.改变元素的位置(sort(),unique())
4.将改变之后的序列,或者序列,保存在其他的迭代中(replace(),replace_copy())

发布了54 篇原创文章 · 获赞 6 · 访问量 3319

猜你喜欢

转载自blog.csdn.net/zengqi12138/article/details/104292237