【c++ primer读书笔记】【第10章】泛型算法

1、泛型算法本身不会执行容器的操作,只会运行于迭代器之上,执行迭代器的操作。算法永远不会改变底层容器的大小。

2、只读算法:一些算法只会读取其输入范围内的元素,从不改变元素。 对于只读算法,最好使用次cbegin()和cend()。

find:接受三个参数,前两个指出查找的元素的范围,第三个参数是要查找的元素。返回指向要查找元素的迭代器,若没找到指定元素,返回尾后迭代器。

#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;

int main(){
	vector<int> vec = { 27, 210, 12, 47, 109, 83 };
	int val = 83;
	auto result = find(vec.cbegin(), vec.cend(), val);
	cout << *result << endl;         //输出83

	system("pause");
	return 0;
}

accumulate:接受三个参数,前两个指出需要求和的元素的范围,第三个参数是和的初值。返回求和的结果。

#include<iostream>
#include<algorithm>
#include<numeric>
#include<vector>
#include<string>
using namespace std;

int main(){
	vector<int> vec{ 1, 2, 3, 4 };
	int val = 0;
	auto result = accumulate(vec.cbegin(), vec.cend(), val);
	cout << result << endl; //输出10
	//string定义了+运算符,能用accumulate函数将string元素连接起来
	vector<string> svec{ "aaa", "bbb", "vvv" };
	auto sresult = accumulate(svec.cbegin(), svec.cend(), string(""));
	cout << sresult << endl;  //输出aaabbbvvv

	system("pause");
	return 0;
}

equal:接受三个参数,前两个指出第一个序列中的元素范围,第三个参数表示指向第二个序列首元素的迭代器。判断两个序列是否保存相同的值。假定第二个序列至少与第一个序列一样长。

#include<iostream>
#include<algorithm>
#include<vector>
#include<list>
using namespace std;

int main(){
	vector<int> vec{ 1, 2, 3, 4 };
	list<int> li{ 1, 2, 3, 4 };
	cout << equal(vec.begin(), vec.end(), li.begin()) << endl; //输出1
	cout << equal(vec.begin() + 1, vec.end(), li.begin()) << endl; //输出0
	system("pause");
	return 0;
}

3、  写容器元素算法:必须确保序列原大小不小于要求写入的元素数目。

fill:接受三个参数,前两个指出序列中的元素范围,第三个参数表示将这个值赋予序列中的每个元素。

fill_n:接受一个单迭代器、一个计数值和一个值。将给定值赋予迭代器指向的元素开始的指定个元素。

#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;

int main(){
	vector<int> vec{ 1, 2, 3, 4 };
	fill(vec.begin(), vec.begin() + vec.size() / 2, 0);
	for (const auto& c : vec)
		cout << c << " ";
	cout << endl;   //输出0 0 3 4
	//向目的位置迭代器写入数据的算法假定目的位置足够大,能容纳要写入的元素
	fill_n(vec.begin(), 3, 1);
	for (const auto& c : vec)
		cout << c << " ";
	cout << endl;   //输出1 1 1 4

	system("pause");
	return 0;
}

back_inserter:接受一个指向容器的引用,返回一个与该容器绑定的插入迭代器。

#include<iostream>
#include<algorithm>
#include<vector>
#include<iterator>
using namespace std;

int main(){
	vector<int> vec;
	fill_n(back_inserter(vec),5,0);
    for(const auto& c:vec)
		cout<<c<<" ";
	cout<<endl; //输出0 0 0 0 0 
	system("pause");
	return 0;
}

4、  拷贝算法

copy:接受三个迭代器,前两个表示输入范围,第三个表示目的序列的起始位置。把输入范围中的元素拷贝到目的序列中。

#include<iostream>
#include<algorithm>
using namespace std;

int main(){
	int ia1[]={1,2,3,4};
	int ia2[sizeof(ia1)/sizeof(*ia1)];
	auto ret=copy(begin(ia1),end(ia1),ia2);//ret指向拷贝到ia2的尾元素之后的位置
	for(const auto& i:ia2)
		cout<<i<<" ";
	cout<<endl;   //输出1 2 3 4
	system("pause");
	return 0;
}

5、  重排容器元素算法

sort:接受两个迭代器,表示排序的元素范围

unique:将相邻的重复项“消除”,并返回一个指向不重复值范围末尾的迭代器。此位置之后的元素仍存在,但不知道值是什么。

#include<iostream>
#include<algorithm>
#include<string>
#include<sstream>
#include<vector>
using namespace std;

int main(){
	string str="the quuick red fox jumps over the slow red turtle";
	stringstream ss(str);
	string word;
	vector<string> vec;
	while(ss>>word)
		vec.push_back(word);

	sort(vec.begin(),vec.end());
	for(const auto& i:vec)
		cout<<i<<" ";
	cout<<endl;   //输出fox jumps over quuick red slow the the turtle

	auto end_unique=unique(vec.begin(),vec.end());
	vec.erase(end_unique,vec.end());
	for(const auto& i:vec)
		cout<<i<<" ";
	cout<<endl;   //输出fox jumps over quuick red slow the turtle

	system("pause");
	return 0;
}

6、谓词是一个可调用的表达式,返回结果是能用作条件的值。谓词分一元谓词和二元谓词。

#include<iostream>
#include<algorithm>
#include<string>
#include<sstream>
#include<vector>
using namespace std;
//二元谓词,比较函数,按长度排序单词
bool isShorter(const string& s1,const string& s2){
	return s1.size()<s2.size();
}
int main(){
	string str="the quuick red fox jumps over the slow red turtle";
	stringstream ss(str);
	string word;
	vector<string> vec;
	while(ss>>word)
		vec.push_back(word);
	vector<string> vec2(vec);

	sort(vec.begin(),vec.end(),isShorter);//按长度由短至长排序
	for(const auto& i:vec)
		cout<<i<<" ";
	cout<<endl;   //输出the red fox the red over slow jumps quick turtle

	system("pause");
	return 0;
}
7、lambda表达式具有如下形式

[capture list](parameter list) -> return type { function body }

capture list(捕获列表)是一个lambda所在函数中定义的局部变量的列表(通常为空)。return type、parameter list和function body分别表示返回类型(必须是尾置返回类型)、参数列表和函数体。可以忽略参数列表和返回类型,但必须永远包含捕获列表和函数体。

与上面isShorter函数完成相同功能的lambda:

[](const string& s1,const string& s2){ return s1.size()<s2.size(); }

lambda值捕获

void fun1(){
	size_t v1=42;
	auto f=[v1]{ return v1; };
	v1=0;
	auto j=f(); //j为42,f保存了我们创建它时v1的拷贝
}
lambda引用捕获
void fun2(){
	size_t v1=42;
	auto f=[&v1]{ return v1; };
	v1=0;
	auto j=f(); //j为0,f保存了v1的引用
}
lambda隐式捕获:&采用引用捕获方式,=采用值捕获方式
[=](const string& s1,const string& s2){ return s1.size()>=size; }
可变lambda:在参数列表首加上关键字mutable,改变被捕获变量的值
void fun3(){
	size_t v1=42;
	auto f=[v1] () mutable { return v1; };
	v1=0;
	auto j=f(); //j为43
}
8、 参数绑定

bind函数在头文件functional中,调用bind函数的一般形式为:

auto newCallable=bind(callable,arg_list);

newCallable本身是一个可调用的对象,arg_list是一个逗号分割的参数列表,对应给定callable的参数。

arg_list可能包含形如_n的名字,n是一个整数,这些参数是占位符,表示了newCallable的参数,它们占据了传递给newCallable的参数的位置。数值n表示生成的可调用对象中参数的位置。_n定义在一个名为placeholders的命名空间中。

bind拷贝其参数,当希望传递给bind一个对象又不拷贝它,必须使用ref函数。

#include<iostream>
#include<functional>
using namespace std;
using namespace std::placeholders;

int add(int a,int b,int c,int d){
	return a+b+c+d;
}
int main(){
	int a=1,c=2;
	auto g=bind(add,a,_2,c,_1);
	cout<<g(3,4)<<endl; //调用add(1,4,2,3);

	system("pause");
	return 0;
}

9、 插入迭代器

back_inserter 创建一个使用push_back的迭代器 。

front_inserter 创建一个使用push_front的迭代器。

inserter创建一个使用insert的迭代器,此函数接受两个参数,这个参数必须是指向给定容器的迭代器。元素将被插入到给定迭代器所表示的元素之前。

#include<iostream>
#include<list>
#include<iterator>
using namespace std;

int main(){
	list<int> lst{ 1, 2, 3, 4 };
	list<int> lst2, lst3, lst4;

	copy(lst.cbegin(), lst.cend(), back_inserter(lst2));
	for (const auto& l : lst2)
		cout << l << " ";
	cout << endl; //输出1 2 3 4

	copy(lst.cbegin(), lst.cend(), front_inserter(lst3));
	for (const auto& l : lst3)
		cout << l << " ";
	cout << endl; //输出4 3 2 1

	copy(lst.cbegin(), lst.cend(), inserter(lst4, lst4.begin()));
	for (const auto& l : lst4)
		cout << l << " ";
	cout << endl;  //输出1 2 3 4

	system("pause");
	return 0;
}

10iostream迭代器

istream_iterator迭代器:读取输入流

ostream_iterator迭代器:向输出流写数据

#include<iostream>
#include<list>
#include<iterator>
using namespace std;

int main(){
	list<int> lst;
	istream_iterator<int> in(cin),eof;
	while(in!=eof)
		lst.push_back(*in++);//解引用迭代器,获得从流读取的前一个值

	ostream_iterator<int> out(cout," ");
	for(const auto& l:lst)
		*out++=l; //将元素写到cout
	cout<<endl;

	system("pause");
	return 0;
}

11、反向迭代器

反向迭代器就是在容器中从尾元素向首元素反向移动的迭代器。对于反向迭代器,++iter会移动到前一个元素,--iter会移动到下一个元素。除了forward_list之外,其他的容器都定义了反向迭代器。

下图显示了一个名为vec的vector上的4种迭代器:

【c++ primer读书笔记】【第10章】泛型算法_第1张图片

#include<iostream>
#include<string>
#include<iterator>
#include<algorithm>
using namespace std;

int main(){
	string str="FIRST,MIDDLE,LAST";
	auto comma=find(str.cbegin(),str.cend(),',');
	cout<<string(str.cbegin(),comma)<<endl; //输出FIRST

	auto rcomma=find(str.crbegin(),str.crend(),',');
	cout<<string(rcomma.base(),str.cend())<<endl;//输出LAST

	system("pause");
	return 0;
}

用图表示上面各个迭代器:

【c++ primer读书笔记】【第10章】泛型算法_第2张图片

12、迭代器类别

输入迭代器:只读,不写,单遍扫描,只能递增

输出迭代器:只写,不读,单遍扫描,只能递增

前向迭代器:可以读写,多遍扫描,只能向前

双向迭代器:可以读写,多遍扫描,可以递增递减

随机访问迭代器:可读写,多遍扫描,支持全部迭代器运算。

13、 list forward_list 的特定算法

链表可以改变元素间的链接而不是真的交换它们的值来“交换”元素,因此特定算法的性能比对应的通用版本好很多。

#include<iostream>
#include<list>
using namespace std;

int main(){
	list<int> lst1,lst2;
	for(size_t i=1;i!=5;++i)
		lst1.push_back(i);
	for(size_t i=6;i!=10;++i)
		lst1.push_back(i);
	lst1.merge(lst2); //将lst2的元素合并入lst1,并将元素从lst2删除,lst1和lst2必须有序
	for(const auto l:lst2)
		cout<<l<<" ";
	cout<<endl;

	lst1.remove(5); //将lst1的指定元素删除
	lst1.reverse();  //将lst1的元素反转
	for(const auto l:lst1)
		cout<<l<<" ";
	cout<<endl;

	list<int> lst3;
	for(size_t i=20;i!=25;++i)
		lst3.push_back(i);
	lst1.splice(lst1.begin(),lst3);//将lst3中的所有元素移动到lst1.begin()之前的位置,并将元素从lst3删除
	for(const auto l:lst1)
		cout<<l<<" ";
	cout<<endl;

	system("pause");
	return 0;
}

你可能感兴趣的:(【c++ primer读书笔记】【第10章】泛型算法)