C++ Primer(泛型算法)

来源:互联网 发布:电子相册在线制作软件 编辑:程序博客网 时间:2024/04/28 08:20

第11章 泛型算法

泛型算法本身从不执行容器操作,只是单独依赖迭代器和迭代器操作实现。

11.2初窥算法

泛型算法包含algorithm头文件,泛化的算术算法包含numeric头文件。

11.2.1只读算法

在numeric中定义的accumulate只读算法

int sum=accumulate(vec.begin(),vec.end(),42);  //vec元素之和加上42

string sum=accumulate(v.being(),v.end(),string"");  //把string元素连接起来

find_first_of在第一段范围内查找与第二段范围中任意元素匹配的元素,返回一个迭代器,指向第一个匹配的元素,否则返回第一个范围的end迭代器

size_t cnt=0;

list<string>::iterator it=roster1.begin();

while((it=find_first_of(it,roster1.end(),roster2,begin(),roster2.end()))!=roster1.end()){

++cnt;

++it;

}

cout<<"found "<<cnt<<"names on both rosters"<<endl;

在while的第一次循环中,遍历整个roster1范围。第二次及后续的循环迭代只考虑roster1中尚未匹配的部分。

11.2.2写容器元素的算法

fill(vec.begin(),vec.end(),0);  //每个元素置0

back_inserter包含在iterator头文件中

fill_n (back_inserter(vec),10,0);  //不检查写入

copy(ilst.begin(),ilst.end(),back_inserter(ivec));  //从输入范围读取元素复制给ivec,效率差,等价与更优的下式

vector<int> ivec(ilst.begin(),ilst.end());

replace(ilst.begin(),ilst.end(),0.42);  //将所有的0替换为42

replace_copy(ilst.begin(),ilst.end(),back_inserter(ivec),0,42);//ilst不变,ivec存储ilst的副本,其中的0都变成了42

11.2.3对容器元素的重新排序的算法

1.去除重复

sort(words.begin(),words.end());  //两个迭代器指出排序访问,使用小于(<)操作符比较元素。

2.unique的使用

该算法删除相邻的重复元素,实际上是将无重复的元素复制到序列的前端。不改变容器的大小。

3.stable_sort的第三个参数使用一个谓词函数,是words中的元素按长度大小排序,长度相同的单词保持字典顺序。

stable_sort(words.begin(),words.end(),isShorter);

4.conut_if算法返回使谓词函数返回条件成立的元素的个数。

vector<string>::size_type wc=count_if(words.begin(),words.end(),GT6);

//全部代码

#include "stdafx.h"

#include <iostream>

#include <vector>

#include <string>

#include <algorithm>

#include <numeric>

using namespace std; 

bool isShorter(  const string &s1, const string &s2 )

{

         return s1.size() < s2.size();

bool GT6( const string &s )

{

         return s.size() >= 6;

}

string make_plural( size_ti, const string &s1,const string &s2 )

{

         return ( i == 1 ) ? s1 : s1 + s2;

}

int _tmain(int argc, _TCHAR* argv[])

{

         vector<string>strVec;

         string strVal;

         while ( cin >>strVal )

                  strVec.push_back( strVal);

         sort ( strVec.begin(), strVec.end() );

         vector<string>::iterator end_unique= unique ( strVec.begin(), strVec.end() );

         strVec.erase( end_unique, strVec.end() );

         stable_sort( strVec.begin(), strVec.end(), isShorter );

         vector<string>::size_type wc = count_if ( strVec.begin(), strVec.end(), GT4 );

         cout << wc<< " " << make_plural ( wc,"word","s" ) <<" 4 letters or longer. " << endl;

         return 0;

}

11.3再谈迭代器

11.3.1插入迭代器

三种插入迭代器:back_inserter,front_inserter,inserter

vector没有pust_front所以不能使用front_inserter

inserter函数在迭代器实参标明的位置前面插入新元素

list<int> ilst,ilst2,ilst3;

for(list<int>::size_type i=0;i!=4;++i)

ilst.push_front(i);

copy(ilst.begin(),ilst.end(),front_inserter(ilst2));//ilst2为0 1 2 3 

copy(ilst.begin(),ilst.end(),inserter(ilst3,ilst3.begin()));//ilst3为3 2 1 0 

11.3.2 iostream迭代器

1.istream_iterator<T> in(strm);创建从输入流 strm 中读取 T 类型对象的 

istream_iterator 对象istream_iterator<T> in; istream_iterator 对象的超出末端迭代器

ostream_iterator<T> in(strm); 创建将 T 类型的对象写到输出流 strm 的 ostream_iterator 对象

ostream_iterator<T> in(strm, delim); 创建将 T 类型的对象写到输出流 strm 的 ostream_iterator 对象,在写入过程中使用 delim 作为元素的分隔符。delim 是以空字符结束的字符数组
流迭代器只定义了最基本的迭代器操作:自增、解引用和赋值,比较两个istream迭代器是否相等,ostream没有比较运算。

istream_iterator<string> in_iter(cin),eof;

ostream_iterator<string> out_iter(cout,"\n");

while(in_iter!=eof)

*out_iter++=*in_iter++;  //读cin,并将每个读入的值依次写到cout中不同的行。
2.流迭代器的限制:
A.不可能从 ostream_iterator 对象读入,也不可能写到 istream_iterator 对象中。
B.一旦给 ostream_iterator 对象赋了一个值,写入就提交了。赋值后,没有办法再改变这个值。此外,ostream_iterator 对象中每个不同的值都只能正好输出一次。
C.ostream_iterator 没有 -> 操作符

3.算法中使用流迭代器

istream_iterator<int> cin_it(cin),eof;

ostream_iterator<int> output(cout," ");

vector<int> ivec(cin_it,eof);

sort(ivec.begin(),ivec.end());

unique_copy(ivec.begin(),ivec.end(),output);


输入:1 1 1 2 2 3 3 4 4 5 输出:1 2 3 4 5

11.3.3反向迭代器

流迭代器不支持自减运算,不能使用反向迭代器

普通迭代器与反向迭代器之间的关系是为了适应左闭合范围这个性质的,所以,对于vector line, [line.rbegin(), rcomma) 和 [rcomma.base(), line.end()) 标记的是 line 中的相同元素。


11.3.4 const迭代器

使用const迭代器则无法用来修改容器中的元素。
1.同时要注意const迭代器在使用过程中的细微差别:用来指定范围的两个迭代器,如果一个该容器的const迭代器,另一个是普通的迭代器,则无法编译通过,因为两个迭代器的类型不同。
2.如果该容器是 const 对象,则返回的迭代器是 const_iterator 类型;否则,就是普通的 iterator 类型。

11.3.5 五种迭代器

Input iterator(输入迭代器): 读,不能写;只支持自增运算;相等和不等操作符(==,!=);解引用操作符;
Output iterator(输出迭代器) 写,不能读;只支持自增运算;解引用操作符;
Forward iterator(前向迭代器) 读和写;只支持自增运算;相等和不等操作符(==,!=);解引用操作符;
Bidirectional iterator(双向迭代器) 读和写;支持自增和自减运算;相等和不等操作符(==,!=);解引用操作符;
Random access iterator(随机访问迭代器) 读和写;关系操作符 <、<=、> 和 >= ;支持自增和自减运算;相等和不等操作符(==,!=);解引用操作符;下标操作符 iter[n] ;两个迭代器之间的减法操作符(--),得到两个迭代器间的距离 ;迭代器与整型数值 n 之间的加法和减法操作符 +、+=、- 和 -=,结果是迭代器在容器中向前(或退回)n 个元素。


map、set和list提供双向迭代器,而string、vector和deque容器都提供随机访问迭代器,用作访问内置数组元素的指针也是随机访问迭代器。istream_iterator是输入迭代器,ostream_iterator是输出迭代器。

11.4 泛型算法的结构

1.根据对元素的操作将算法分为下面几种:
A.只读算法,不改变元素的值顺序。
B.给指定元素赋新值的算法。
C.将一个元素的值移给另一个元素的算法。
C++ 还提供了另外两种算法模式:一种模式由算法所带的形参定义;另一种模式则通过两种函数命名和重载的规范定义。

2.算法的形参模式:大多数算法采用下面四种形式之一:
alg (beg, end, other parms);
alg (beg, end, dest, other parms);//dest 形参是一个迭代器,用于指定存储输出数据的目标对象。算法假定无论需要写入多少个元素都是安全的。
alg (beg, end, beg2, other parms);
alg (beg, end, beg2, end2, other parms);//算法同时使用 beg2 和 end2 时,这些迭代器用于标记完整的第二个范围:带有 beg2 而不带 end2 的算法将 beg2 视为第二个输入范围的首元素,但没有指定该范围的最后一个元素。这些算法假定以 beg2 开始的范围至少与 beg 和 end 指定的范围一样大。
3.区别带有一个值或一个谓词函数参数的算法版本:
A.这些算法通常要用到标准关系操作符:== 或 <。其中的大部分算法会提供第二个版本的函数,允许程序员提供比较或测试函数取代操作符的使用.
B.重新对容器元素排序的算法要使用 < 操作符。这些算法的第二个重载版本带有一个额外的形参,表示用于元素排序的不同运算:
sort (beg, end); // use < operator to sort the elements
sort (beg, end, comp); // use function named comp to sort the elements
C.检查指定值的算法默认使用 == 操作符。系统为这类算法提供另外命名的(而非重载的)版本,带有谓词函数形参。带有谓词函数形参的算法,其名字带有后缀 _if:
find(beg, end, val); // find first instance of val in the input range
find_if(beg, end, pred); // find first instance for which pred is true
4.区别是否实现复制的算法版本:无论算法是否检查它的元素值,都可能重新排列输入范围内的元素。在默认情况下,这些算法将重新排列的元素写回其输入范围。标准库也为这些算法提供另外命名的版本,将元素写到指定的输出目标。此版本的算法在名字中添加了 _copy 后缀:
reverse(beg, end);
reverse_copy(beg, end, dest);

11.5容器特有的算法

list 容器上的迭代器是双向的,而不是随机访问类型。由于 list 容器不支持随机访问,因此,在此容器上不能使用需要随机访问迭代器的算法。这些算法包括 sort 及其相关的算法。还有一些其他的泛型算法,如 merge、remove、reverse 和 unique,虽然可以用在 list 上,但却付出了性能上的代价。如果这些算法利用 list 容器实现的特点,则可以更高效地执行。

1.list 容器特有的算法与其泛型算法版本之间有两个至关重要的差别。其中一个差别是 remove 和 unique 的 list 版本修改了其关联的基础容器:真正删除了指定的元素。例如,list::unique 将 list 中第二个和后续重复的元素删除出该容器。与对应的泛型算法不同,list 容器特有的操作能添加和删除元素。

2.另一个差别是 list 容器提供的 merge 和 splice 运算会破坏它们的实参。使用 merge 的泛型算法版本时,合并的序列将写入目标迭代器指向的对象,而它的两个输入序列保持不变。但是,使用 list 容器的 merge 成员函数时,则会破坏它的实参 list 对象——当实参对象的元素合并到调用 merge 函数的 list 对象时,实参对象的元素被移出并删除。

3.list 容器特有的操作:
lst.merge(lst2)   lst.merge(lst2, comp)
将 lst2 的元素合并到 lst 中。这两个 list 容器对象都必须排序。lst2 中的元素将被删除。合并后,lst2 为空。返回 void 类型。第一个版本使用 < 操作符,而第二个版本则使用 comp 指定的比较运算

lst.remove(val) lst.remove_if(unaryPred) 调用 lst.erase 删除所有等于指定值或使指定的谓词函数返回非零值的元素。返回 void 类型

lst.reverse() 反向排列 lst 中的元素

lst.sort 对 lst 中的元素排序

lst.splice(iter, lst2)
lst.splice(iter, lst2, iter2)
lst.splice(iter, beg, end)
将 lst2 的元素移到 lst 中迭代器 iter 指向的元素前面。在 lst2 中删除移出的元素。第一个版本将 lst2 的所有元素移到 lst 中;合并后,lst2 为空。lst 和 lst2 不能是同一个 list 对象。第二个版本只移动 iter2 所指向的元素,这个元素必须是 lst2 中的元素。在这种情况中,lst 和 lst2 可以是同一个 list 对象。也就是说,可在一个 list 对象中使用 splice 运算移动一个元素。第三个版本移动迭代器 beg 和 end 标记的范围内的元素。beg 和 end 照例必须指定一个有效的范围。这两个迭代器可标记任意 list 对象内的范围,包括 lst。当它们指定 lst 的一段范围时,如果 iter 也指向这个范围的一个元素,则该运算未定义。

lst.unique() lst.unique(binaryPred)
调用 erase 删除同一个值的团结副本。第一个版本使用 == 操作符判断元素是否相等;第二个版本则使用指定的谓词函数实现判断



0 0
原创粉丝点击