stl学习之泛型算法

来源:互联网 发布:淘宝上图教程 编辑:程序博客网 时间:2024/04/29 20:46

算法概述

stl容器定义的算法非常少,如排序、查找特定元素等有用的算法,全部是放在算法库中的,这些算法对所有的容器都有效,所以称之为泛型算法。

在使用泛型算法前,需要包含算法的头文件:
#include <algorithm>

#include <numeric>//算术算法

除少数算法以外,所有算法都在一段范围内的元素上操作,称这段范围为“输入范围”。而输入范围是由两个迭代器组成的(左闭合区间)。

理解算法最基本的方法是:

1、了解该算法是否读元素

2、是否写元素

3、是否对元素排序

 

只读算法

许多算法只会读取范围内的元素,但不会去写这些元素。来看看accumulate算法,这是一个用于累加的算法。

来看一个例子:

int sum=accumulate(vec.begin(),vec.end(),0);

以上是将sum的值设置为vec所有元素之和再加上0,这里的0是必要的,因为算法要靠这个0来确定将要累加的元素的类型,并将这个0作为累加的起点。

 

再看一个复杂点的例子:

find_first_of算法,这个算法带有两对迭代器参数,来标记两段元素范围,在第一段范围内查找与第二段范围中任意元素匹配的元素,然后返回一个迭代器,指向第一段中第一个匹配的元素。如果找不到匹配元素,则返回第一个范围内的end迭代器。

举个例子:

有两个序列:

1,2,3,4,6;

5,6,8,4,6;

对于这两个序列,调用这个算法后,将返回指向第一个序列中的4的迭代器。因为对于第一个序列来说,前三个数字都无法与第二个序列的任意数相等。虽然6也是符合条件的,但此算法返回的是第一个相等元素的迭代器。如果想统计出所有相等的元素,则可以使用一个循环来实现。

vector<int>::iterator iter=ivec1.begin();

while(( iter=find_first_of(iter,ivec1.end(),ivec2.begin(),ivec2.end()) )!=ivec1.end())

{

   ...//对每个相等的元素处理

   ++iter;

}

 

写入算法

一些算法需要写入元素值,使用这些算法时必须小心,要确保算法所写的序列要足够的大,可以容纳要写入的元素。

有些算法是直接将数据写入到输入序列(参数中明确指明了要写入的序列范围),例如:

fill(ivec.begin(),ivec.end(),0);

fill带有一对迭代器形参,用于指定要写入的范围,而所写入的值,正是第三个参数的副本。以上的例子,将ivec中所有元素的值设置为0。如果输入的范围有效,则可以安全写入。

 

fill算法是明确标明了一个写入范围,从而确保了写入的安全性。看一个反例。

vector<int> ivec;

fill_n(ivec.begin(),10,0);

以上操作将会导致无法预测的结果。

原因在于,fill_n函数带有一个迭代器形参,一个计数器及一个值。该函数从迭代器指向的元素开始,将指定数量的元素设置为给定的值。fill_n函数假定对指定数量的元素做写操作是安全的。

对于上述例子,ivec是一个空容器,里面没有任何元素,而fill_n本身是不做安全检查的。

 

为了解决以上问题,我们引入back_inserter类型的迭代器。

 

back_inserter迭代器

确保算法有足够的元素存储输出数据的办法,是使用back_inserter迭代器(插入迭代器)。

back_inserter迭代器的性质是,使用插入迭代器赋值时,会在容器中添加一个新元素,其值等于赋值运算的右操作数的值。

back_inserter迭代器是一种迭代器适配器,也就是说,back_inserter的参数是一个对象的引用。举个例子:back_inserter(ivec)这将生成一个绑定在ivec容器上的插入迭代器。在试图给这个迭代器赋值时,赋值运算将会调用push_back在容器中添加一个指定值的元素。

那么,以上的fill_n可以这样写:

fill_n(back_inserter(ivec),10,0);

现在,fill_n写入的每一个值,都会通过back_inserter生成的迭代器实现。

 

有些算法提供copy版本,以上所说的几个算法,都会对输入序列做出处理,而copy版本,则是将处理后的序列存储到一个新序列中,而不修改原始输入序列。

看一个例子:

replace(ilist.begin(),ilist.end(),0,42);

replace的作用是将所有值为0的元素替换成42。如不想改变原始序列,则可以使用copy版本。

replace_copy(ilist.begin(),ilist.end(),back_inserter(ivec),0,42);

replace_copy的作用是,ilist不改变,而ivec中存储了ilist的副本,而ilist中的0在ivec中全部替换成了42.

 

排序算法

排序算法是最常见的算法种类之一。

1、sort:作用是将指定范围内的元素,按从小到大的顺序进行排序。

例如:

sort(ivec.begin(),ivec.end());//对ivec中的元素进行排序

2、unique:此函数的作用是,将序列中重复的元素,放到序列的末尾。之后返回一个迭代器,迭代器指向超出无重复的元素范围的下一位置。

例如:

1,2,2,3,4,5,6,3

使用unique后,序列变为:

1,2,3,4,5,6,2,3//2和3在序列中是重复的

而返回的迭代器指向倒数第二个2。也就是说,unique返回的迭代器指向元素之前的所有元素都是无重复的。

3、stable_sort:stable_sort是重载的,有两个版本。其中一个和sort一样,带有一对迭代器参数。另一个需要一对迭代器和一个谓词函数。

举个例子:

bool UDgreater(int elem1,int elem2)

{//这就是谓词函数,定义了此谓词函数,然后再stable_sort中调用,则可以使序列从大到到小排列

    return elem1>elem2;

}

...

stable_sort(ivec.begin(),ivec.end(),UDgreater);

这两个版本的区别在于:

带有迭代器对的版本是按照默认的严格弱排序,也就是从小到大排列。

而带有谓词函数的版本则可以自己定义排序方式,如UDgreater,从大到小排列。

4、count_if:参数为两个迭代器及一个谓词函数。count_if对每一个元素应用谓词函数,并返回使谓词函数为真的元素个数

举个例子:

bool greater10(int value)
{//统计大于10的元素个数
    return value >10;
}

...

int num=count_if(ivec.begin(),ivec.end(),greater10);