大多数算法定义在头文件algorithm中,在头文件numeric中定义了数值泛型算法。
以find算法为例:在容器的两个迭代器指定的范围内遍历,查找特定值。
1 int val=44; 2 auto result=find(ivec.begin(),ivec.end(),val); 3 cout<
迭代器令算法不依赖于容器,但算法依赖于元素类型的操作
算法不执行容器的操作,只会执行迭代器的操作
只读算法:
只会读取其范围之内的元素而不会改变元素
除了find算法之外,例如accumulate算法(定义在numeric头文件中),返回范围内元素之和+指定初值
cin>>sum;
accumulate(ivec.cbegin(),ivec.cend(),sum);(如果是string类型,则是将string连接起来)
对于只读算法,最好选取cbegin和cend作为迭代器
再举操作两个序列的算法equal为例:(如果两个序列所有对应元素相同,则返回true,否则返回false)
equal(ivec1.cbegin(),ivec1.cend(),ivec2.cbegin());
equal算法并不要求两个序列元素类型严格一致,只要能用==比较即可
基于此例,对于只给出第二个序列的单一迭代器的算法,都假定两序列等长(至少不能比第一个短)
写元素算法:
需要确保容器自身大小至少不小于写入的元素数目(因为算法不能改变容器大小)
例如:fill(ivec.begin(),ivec.end(),val);将容器每个值赋为val
fill_n(ivec.begin(),ivec.size(),val);将容器每个值赋为val
注意:如果对空容器使用fill_n并且指定长度,会引起错误
back_inserter(定义在iterator头文件)
为了向容器尾部添加元素而使用的迭代器
1 vector<int> ivec; 2 auto it =back_inserter(ivec); 3 *it=23;
这样在容器的尾部添加了一个元素,值为23
结合fill_n使用:(添加了10个值为val的元素到ivec)
1 vector<int> ivec; 2 fill_n(back_inserter(ivec),10,val);
拷贝算法:
1 auto ret=copy(begin(arr1),end(arr1),arr2);
这样将arr1序列的值拷贝给arr2(要求arr2至少和arr1等长),返回end(arr2)
类似copy算法,replace也具备此类替换效果:
1 replace(ilst.begin(),ilst.end(),search_value,new_value);
这样将ilst序列中所有值为search_value的元素赋值为new_value
如果不想将原序列改变,可以将元素替换后的序列保存在其他容器中:
1 replace_copy(ilst.begin(),ilst.end(),back_inserter(ivec),search_value,new_value);
这样借助replace_copy函数和back_inserter将新的ilst保存在ivec中,原本的ilst不变
重排元素算法:
1 #include2 #include 3 #include 4 #include 5 #include<string> 6 #include 7 using namespace std; 8 void elimDups(vector<string> &vec){ 9 //使向量按字典序排序 10 sort(vec.begin(),vec.end()); 11 //使用unique函数重排序列,使得序列中每个词仅出现一次 12 //获得的序列在原序列的前部,返回不重复元素的子序列的尾后迭代器 13 auto end_unique=unique(vec.begin(),vec.end()); 14 //使用erase函数删除重复单词 15 vec.erase(end_unique,vec.end()); 16 } 17 int main(){ 18 string str; 19 vector<string> svec; 20 while(getline(cin,str)){ 21 svec.push_back(str); 22 } 23 for(const auto &i:svec){ 24 cout<" "; 25 } 26 elimDups(svec); 27 cout<<endl; 28 for(const auto &j:svec){ 29 cout< " "; 30 } 31 32 } 33 /*output: 34 the 35 quick 36 red 37 fox 38 jumps 39 over 40 the 41 slow 42 red 43 turtle 44 ^Z 45 the quick red fox jumps over the slow red turtle 46 fox jumps over quick red slow the turtle*/
定制操作:
例如使sort接受第三个参数,此参数即为谓词(可调用的表达式,返回值可作为条件),原本sort利用<来比较元素,那么这里使用谓词来代替<作为比较的依据
1 bool isShorter(const string &s1,const string &s2){ 2 return s1.size()<s2.size();} 3 sort(svec.begin(),svec.end(),isShorter);
这里用长度来比较字符串,而不是以字典序作为比较依据
如果我们希望将等长的字符串以字典序比较,不等长的字符串以长度比较:
1 void elimDups(vector<string> &vec){ 2 //使向量按字典序排序 3 sort(vec.begin(),vec.end()); 4 //使用unique函数重排序列,使得序列中每个词仅出现一次 5 //获得的序列在原序列的前部,返回不重复元素的子序列的尾后迭代器 6 auto end_unique=unique(vec.begin(),vec.end()); 7 //使用erase函数删除重复单词 8 vec.erase(end_unique,vec.end()); 9 } 10 int main(){ 11 elimDups(svec); 12 stable_sort(svec.begin(),svec.end(),isShorter); 13 }
这里使用了提供谓词的stable_sort函数,保持了等长的元素之间的字典序
lambda表达式:(c++11)格式:[capture list](parameter list)->return type{function body};
这里capture list是函数中定义的局部变量的列表(通常为空)
建立调用对象时可以省略列表 (相当于参数为空)和返回类型(除非函数体中存在返回语句,否则默认为void),如:
1 auto f = [ ]{return 23;}; f();
不同于普通函数,lambda不能有默认参数
例如:等价于上述isShorter函数的lambda:
[](const string &s1,const string &s2){ return s1.size()
[ ]内为空说明lambda不使用函数内任何局部变量
如果capture list 不为空,则函数体可以使用capture list中的变量:
[sz](const string&s1){ return s1.size()
完整的biggies函数:包含find_if函数和for_each函数
void biggies(const string &s1,const string::size_type sz){ elimDups(svec); stable_sort(svec.begin(),svec.end(),[](const string &s1,const string &s2){ return s1.size()<s2.size();}); auto wc=find_if(svec.begin(),svec.end(),[sz](const string &s){ return s.size()>=sz}); auto count = svec.end()-wc; for_each(wc,svec.end(),[](const string &s){cout<' ';}); }
这里find_if函数返回指向第一个长度不小于给定参数sz的元素的迭代器
这里for_each函数接受一个可调用对象,并输出序列中每个元素调用此对象
lambda的捕获和返回:
每当定义一个lambda时,编译器自动生成一个未命名的类类型,
每当向函数传递一个lambda,同时定义了一个新类型和该类型的一个对象。
与函数调用参数类似,lambda的capture list可以是值捕获也可以是引用捕获(其效应与函数参数别无二致)
(注意:应尽量避免引用捕获或者指针捕获,如果必须如此,必须确保在调用lambda的时期内捕获的对象的值是有效的)
与显式捕获相对应的,也可以使用隐式捕获:
[=]表示隐式的值捕获,[&]表示隐式的引用捕获
[=,&]表示按照调用顺序,第一个是值捕获,第二个是引用捕获(其余组合以此类推)
可变lambda:
如果希望能够改变被捕获的变量的值,必须在参数列表首加上关键字mutable
auto f = [sz] () mutable{return ++sz;}; 可变lambda的参数列表可以省略
指定lambda返回类型:
例如: transform(ivec.begin(),ivec.end(),ivec.begin(),[](int i)->int{return i<0?i:-1});
这里transform函数将原序列各个元素的值替换为其绝对值(第一个和第三个参数相同==调用和输入序列首是同一位置)
以上说明了lambda的使用方法,一般较为简单的操作适合使用lambda,如果是比较复杂而且重复的操作还是调用函数比较合适
参数绑定:
标准库bind函数:(c++11)(所在头文件functional)
如果我们写了一个函数,并且希望用此函数来代替lambda,使用bind来解决向sz形参传递参数的问题
调用bind的格式:auto newCallable=bind(callable,args_list);
(其中args_list中如果含有诸如_1,_2之类的名字,则用来表示newCallable中对应位置的参数)
具体使用例如:
1 bool check_size(const string &s1,string::size_type sz){ 2 return s1.size()>=sz;} 3 auto check2=bind(check_size,_1,2);
这里_1表示check2的第一个参数(也是唯一一个)就是(check_size的第一个参数)const string&,2表示sz==2
调用check2: string str="HELL"; bool b2=check2(str);(等效于调用check_size(str,6))
bind语句可以作为lambda的替代品
上述的语句中如名字_1,_2之类在使用之前需要声明名字空间:using std::placeholders::_1;之类的语句
(也可以直接写 using namespace std::placeholders;)
bind参数与函数对应的关系:
auto g=bind(f,a,b,_2,c,_1);
调用g(_1,_2);等效于调用f(a,b,_2,c,_1);
(如果调用g(X,Y)等效于调用f(a,b,Y,c,X))
如果希望传递给bind一个引用参数parameter,必须使用ref(parameter)或者cref(parameter)
迭代器再探:
插入迭代器:
支持的操作:it = t;在it指定的位置插入值t
back_inserter(value);创建一个使用push_back的迭代器
front_inserter(value);创建一个使用push_front的迭代器
inserter(value,pos);在pos迭代器之前一个位置插入值为value的元素