C++泛型算法.docx
- 文档编号:3484657
- 上传时间:2022-11-23
- 格式:DOCX
- 页数:15
- 大小:25.35KB
C++泛型算法.docx
《C++泛型算法.docx》由会员分享,可在线阅读,更多相关《C++泛型算法.docx(15页珍藏版)》请在冰豆网上搜索。
C++泛型算法
C++泛型算法
本文主要讨论C++标准库中的泛型算法(genericalgorithm)。
泛型算法是使用容器的强有力的辅助工具。
如果文中有错误或遗漏之处,敬请指出,谢谢!
标准库为容器类型定义的操作很少,并没有为每个容器实现更多的操作。
因为这部分操作可以抽象出来为所有的容器工作,那就是泛型算法。
所谓“泛型”是指这些算法可以应用于多种容器类型上,而容器内的元素类型也可以多样化。
标准库提供了100多个泛型算法,主要定义于头文件
大多数泛型算法是工作于容器的一对迭代器所标识的范围,并完全通过迭代器来实现其功能。
这段由迭代器指定的范围称为“输入范围”。
带有输入范围参数的算法总是使用前两个参数标记该范围,分别指向要处理的第一个元素和最后一个元素的下一个位置。
这些算法一般可划分为只读算法、改写元素算法或对元素重新排序算法,下面分别叙述之。
只读算法
find算法
template
InItfind(InItfirst,InItlast,constT&val);
查询迭代器指定范围[first,last)范围内是否有val值。
如果有,则返回该值对应的迭代器;否则,返回last表示查找失败。
accumulate算法
template
Taccumulate(InItfirst,InItlast,Tval);
template
Taccumulate(InItfirst,InItlast,Tval,Predpr);
累加迭代器指定范围[first,last)范围内所有元素,再加上累加的初值val,返回累加的结果。
第二个函数自定义操作:
val=pr(val,*it)。
注:
用于指定累加起始值的第三个参数是必要的,因为算法对将要累加的元素类型一无所知,没有别的办法创建合适的起始值或者关联的类型。
find_first_of算法
template
FwdIt1find_first_of(FwdIt1first1,FwdIt1last1,FwdIt2first2,FwdIt2last2);
template
FwdIt1find_first_of(FwdIt1first1,FwdIt1last1,FwdIt2first2,FwdIt2last2,Predpr);
查询第一段范围内与第二段范围内任意元素匹配的元素的位置。
如果找到,返回该元素对应的迭代器;否则,返回last1。
第二个函数使用判断:
pr(*it1,*it2)来代替第一个函数中的判断:
*it1==*it2。
写容器元素的算法
在使用写元素的算法时,必须确保算法所写的序列至少足以存储要写入的元素。
有些算法直接将数据写入到输入序列,另外一些则带有一个额外的迭代器参数指定写入目标。
这类算法将目标迭代器用作输出的位置。
还有第三种算法将指定数目的元素写入某个序列。
写入输入序列的元素
写入到输入序列的算法本质上是案例的,因为只会写入与指定输入范围数量相同的元素。
如fill算法:
template
voidfill(FwdItfirst,FwdItlast,constT&x);
这个算法将指定范围内的每个元素都设定为给定的值。
如果输入范围有效,则可以安全写入。
这个算法只会对输入范围内已存在的元素进行写入操作。
不检查写入操作的算法
这类算法如fill_n算法:
template
voidfill_n(OutItfirst,Sizen,constT&x);
该算法从迭代器指向的元素开始,将指定数量的元素设置为给定的值。
如果目标范围内的某些元素不存在,则该操作未定义。
如下面的代码将发生不可预料的结果:
vector
fill_n(vec.begin(),10,0); //disasterbehavior
注:
对指定数目的元素做写入运算,或者写到目标迭代的算法,都不检查目标的大小是否足以存储要写入的元素。
back_inserter
确保算法有足够的元素存储输出数据的一种方法是使用插入迭代器(insertiterator)。
插入迭代器是可以给基础容器添加元素的迭代器。
通常,用迭代器给容器元素赋值时,被赋值的是迭代器所指向的元素。
而使用插入迭代器赋值时,则会在容器中添加一个新元素,其值等于赋值运算的右操作数的值。
back_inserter函数是迭代器适配器,其使用一个对象作为实参,并生成一个适应其实参行为的新对象。
比如,在下例中,传递给back_inserter的实参是一个容器的引用。
back_inserter生成一个绑定在该容器上的插入迭代器。
在试图通过这个迭代器给元素赋值时,赋值运算将调用push_back在容器中添加一个具有指定值的元素。
因此,用back_inserter改写上面的代码可以有效地工作:
vector
fill_n(back_inserter(vec),10,0); //ok:
appends10elementstovec
写入到目标迭代器的算法
第三类算法向目标迭代器写入未知个数的元素。
这类算法最简单的如copy算法:
template
OutItcopy(InItfirst,InItlast,OutItx);
copy算法带有三个迭代器参数:
前两个指定输入范围,第三个指向目标序列的第一个元素。
算法的_copy版本
有些算法提供所谓的“_copy”版本。
这些算法对输入序列的元素做处理,但不修改原来的元素,而是创建一个新序列存储元素的处理结果。
例如,replace算法:
template
voidreplace(FwdItfirst,FwdItlast,constT&vold,constT&vnew);
该算法指定范围[first,last)内的所有元素值为vold替换为vnew。
如果不想改变原序列,可以用replace_copy算法:
template
OutItreplace_copy(InItfirst,InItlast,OutItx,constT&vold,constT&vnew);
这个算法接受第三个迭代器参数,指定保存替换后的序列的目标位置。
例如:
vector
replace(ilist.begin(),ilist.end(),back_inserter(ivec),1,10);
调用该函数后,ilist没有改变,而ivec存储ilist的一份替换后的副本。
对容器元素重新排序的算法
sort算法
这里只介绍sort和stable_sort这个类排序算法:
template
voidsort(RanItfirst,RanItlast);
template
voidsort(RanItfirst,RanItlast,Predpr);
template
voidstable_sort(RanItfirst,RanItlast);
template
voidstable_sort(RanItfirst,RanItlast,Predpr);
sort排序算法是最一般的类型,而stable_sort排序算法是稳定排序。
unique和unique_copy
unique函数“删除”指定范围内的重复元素。
注意:
这里的“删除”不是真正意义上的删除,只是在有重复元素时,把后面的元素向前移动覆盖了原来的元素。
函数返回的迭代器指向无重复元素序列最后一个元素的下一个位置。
而unique_copy是它的“_copy”版本,返回的是生成的序列的最后一个元素的下一个位置。
template
FwdItunique(FwdItfirst,FwdItlast);
template
FwdItunique(FwdItfirst,FwdItlast,Predpr);
template
OutItunique_copy(InItfirst,InItlast,OutItx);
template
OutItunique_copy(InItfirst,InItlast,OutItx,Predpr);
注意:
unique调用后,原序列的前面部分是无重复元素的序列,而后半部分是剩下没有被覆盖的序列。
这里,需要手动删除后面的元素序列,范围由返回的迭代器和容器末端决定。
迭代器
插入迭代器
插入迭代器是一种迭代器适配器,带有一个容器参数,并生成一个迭代器,用于在指定的容器中插入元素。
通过插入迭代器赋值时,迭代器将会插入一个新的元素。
C++语言提供了三种插入器,其差别在于插入元素的位置不同:
1)back_inserter,创建使用push_back实现插入的迭代器;
2)front_inserter,使用push_front实现插入;
3)inserter,使用insert实现插入操作。
除了所关联的容器外,inserter还带有第二个实参:
指向插入起始位置的迭代器。
front_inserter的操作类似于back_inserter:
该函数将创建一个迭代器,调用它所关联的基础容器的push_front成员函数代替赋值操作。
注意:
只有当容器提供push_front操作时,才能使用front_inserter。
在vector或其他没有push_front运算的容器上使用front_inserter,将产生错误。
inserter将产生在指定位置实现插入的迭代器,inserter总是在它的迭代器参数所标明的位置前面插入新元素。
看看下面的例子:
list
//afterthisloopilstcontains:
1234
for(list
:
value_typei=0;i!
=4;++i)
ilst.push_front(i+1);
//aftercopyilst2contains:
4321
copy(ilst.begin(),ilst.end(),front_inserter(ilst2));
//aftercopyilst3contains:
1234
copy(ilst.begin(),ilst.end(),inserter(ilst3,ilst3.begin()));
iostream迭代器
虽然iostream类型不是容器,但标准库同样提供了在iostream对象上使用的迭代器:
istream_iterator用于读取读入流,而ostream_iterator用于写输出流。
这些迭代器将它们所对应的流视为特定类型的元素序列。
使用流迭代器时,可以用泛型算法从流对象中读数据(或将数据写到流对象中)。
istream_iterator
创建从输入流strm中读取T类型对象的istream_iterator对象
istream_iterator
istream_iterator对象的超出末端迭代器
ostream_iterator
创建将T类型的对象写到输出流strm的ostream_iterator对象
ostream_iterator
创建将T类型的对象写到输出流strm的ostream_iterator对象,在写入过程中使用delim作为元素的分隔符。
delim是以空字符结束的字符数组
流迭代器只定义了最基本的迭代器操作:
自增、解引用和赋值。
此外,可比较两个istream迭代器是否相等(或不等)。
而ostream迭代器则不提供比较运算。
it1==it2
比较两个istream_iterator是否相等(不等)。
迭代器读取的必须是
相同的类型。
如果两个迭代器都是end值,则它们相等。
对于两个都不
it1!
=it2
指向流结束位置的迭代器,如果它们使用同一个输入流构造,则它们
相等。
*it
返回从流中读取的值
it->mem
是(*it).mem的同义词。
返回从流中读取的对象的mem成员
++it
通过使用元素类型提供的>>操作符从个输入流中读取下一个元素值,
使迭代器向前移动。
通常,前缀版本使迭代器在流中向前移动,并返
回对加1后的迭代器的引用。
it++
而后缀版本使迭代器在流中向前移动后,返回原值。
流迭代器是类模板:
任何已定义输入操作符(>>操作符)的类型都可以定义istream_iterator。
类似地,任何已定义输出操作符(<<操作符)的类型也可以ostream_iterator。
istream_iterator使用举例:
#include
#include
#include
using namespace std;
int main() {
istream_iterator
istream_iterator
//vector
vector
while (in_iter !
= eof)
vec.push_back(*in_iter++);
vector
:
const_iterator it = vec.begin();
for(; it !
= vec.end(); ++it)
cout<<*it< return 0; } ostream_iterator使用举例: #include #include using namespace std; int main() { ostream_iterator istream_iterator while (in_iter ! = eof) *out_iter++ = *in_iter++; return 0; } 流迭代器的限制: 1)不可能从ostream_iterator对象读入,也不可能写到istream_iterator对象中; 2)一旦给ostream_iterator对象赋了一个值,写入就提交了。 赋值后,没有办法再改变这个值。 此外,ostream_iterator对象中每个不同的值都只能正好输出一次。 3)ostream_iterator没有->操作符。 与算法一起使用流迭代器,如下面的示例实现从标准输入读取一些数,然后将不重复的数写到标准输出: #include #include #include #include using namespace std; int main() { istream_iterator vector sort(vec.begin(), vec.end()); ostream_iterator unique_copy(vec.begin(), vec.end(), out_it); return 0; } 反向迭代器 反向迭代器是一种反向遍历容器的迭代器。 也就是,从最后一个元素到第一个元素遍历容器。 反向迭代器将自增(和自减)的含义反过来了: 对于反向迭代器,++运算将访问前一个元素,而--运算则访问下一个元素。 begin(),end(),rbegin(),rend()与容器序列关系示意图如下: 1)反向迭代器需要使用自减操作符: 标准容器上的迭代器(reverse_iterator)既支持自增运算,也支持自减运算。 但是,流迭代器由于不能反向遍历流,因此流迭代器不能创建反向迭代器。 2)可以通过reverse_iterator: : base()将反向迭代器转换为普通迭代器使用,从逆序得到普通次序。 如下面的例子所示: #include #include #include #include using namespace std; int main() { string str = "this'sentence'isatest"; cout<<"String: "< string: : iterator it1 = find(str.begin(), str.end(), '/''); string: : iterator it2 = find(++it1, str.end(), '/''); //output: sentence cout<<"B-E: "< string: : reverse_iterator rit1 = find(str.rbegin(), str.rend(), '/''); string: : reverse_iterator rit2 = find(++rit1, str.rend(), '/''); //output: ecnetnes cout<<"R-B-E1: "< //output: sentence cout<<"R-B-E2: "< return 0; } const迭代器 在标准库中,有输入范围的泛型算法要求其两个迭代器类型完全一样,包括const属性。 要么都是const,要么都是非const,否则无法通过编译。 同样,它们的返回值迭代器也与参数类型保持一致。 迭代器分类 不同的迭代器支持不同的操作集,而各种算法也要求相应的迭代器具有最小的操作集。 因此,可以将算法的迭代器分为下面五类: 输入迭代器 (inputiterator) 读,不能写。 支持的操作集: ==,! =,前缀++,后缀++,*,->。 例如,find,accumulate算法要求这类迭代器。 输出迭代器 (outputiterator) 写,不能读。 支持的操作集: 前缀++,后缀++,*(只能出现在赋值运算的左操作数上)。 推出迭代器要求每个迭代器必须正好写入一次。 例如,ostream_iterator是输出迭代器,copy算法使用这类迭代器。 前向迭代器(forwarditerator) 读和写,支持输入迭代器和输出迭代器提供的所有操作,还支持对同一个元素的多次读写。 例如,replace算法需要这种迭代器。 双向迭代器(bidirectionaliterator) 读和写,除了支持前向迭代器的所有操作,还支持前缀--和后缀--,即支持双向遍历容器。 例如,reverse算法要求这类迭代器。 标准库容器中提供的迭代器都至少达到双向迭代器的要求。 随机访问迭代器(random-accessiterator) 读和写。 提供在常量时间内访问容器任意位置的功能。 支持完整的迭代器操作集: 1)关系运算: ==,! =,<,<=,>,>=;2)算术运算: it+n,it-n,it+=n,it-=n以及it1-it2;3)下标运算: it[n],等价于*(it+n)。 需要随机访问迭代器的泛型算法包括sort算法。 例如,vector,deque,string迭代器是随机访问迭代器,用作访问内置数组元素的指针也是随机访问迭代器。 除了输出迭代器,其他类别的迭代器形成了一个层次结构: 需要低级类别迭代器的地方,可使用任意一种更高级的迭代器。 例如,对于需要输入迭代器的算法,可传递前向、双向或随机访问迭代器调用该算法。 而反之则不行。 注意: 向算法传递无效的迭代器类别所
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- C+ 算法