C++学习笔记【泛型算法——泛型算法基础】

标准库没有给每种容器添加大量功能,而是提供了一组算法。这些算法大多数都独立于容器。很多时候我们希望对容器做其他操作,比如查找、排序等。标准库没有给每种容器都定义成员函数来实现这些操作,而是定义了一组泛型算法(generic algorithm),它们可用于不同类型的容器和不同类型的元素。

1、简单概念

大多数算法定义在头文件algorithm中,还在头文件numeric中定义了一组数值泛型算法。大多数情况下,这些算法不直接对容器进行操作,而是遍历由两个迭代器所指定的元素范围来操作。

#include<algorithm.h>
#include<numeric.h>
int main()
{
	vector<int>v;
	auto result=find(v.begin(),v.end(),1);
	return 0;
}

如以上代码所示,find的前两个参数是容器的迭代器,第三个参数是想查找的值。

在查找过程中,不需要依赖于容器所保存的元素类型。因为只要有迭代器就可以访问元素。虽然迭代器令算法不依赖于容器,但算法依旧依赖于元素类型的操作,比如说find中会用到==运算符。

2、只读算法

有些算法只会读取元素,而不会改变元素。find就是这样的算法。另一个只读算法是accumulate,定义在numeric头文件中,用于对范围内的元素求和。

int sum=accumulate(v.cbegin(),v.cend(),0);
//前两个参数指定需要求和的元素的范围,第三个参数是和的初值。
//第三个参数的类型决定了返回值的类型

以上算法隐含了一个点:accumulate将第三个参数作为求和的起点,这说明把v中元素加到和上的操作必须是可行的,或者说v中元素能够转换为第三个参数的类型。比如说,上例中v中元素可以是int,double,long long或者其他可加到int上的类型。

//假设s是一个string类型的vector
string sum=accumulate(s.begin(),s.end(),string(""));//正确
sum=accumulate(s.begin(),s.end(),"");				//错误
//因为""是字符串字面值,const char*类型,它没有定义+运算符

注意:对于只读不写元素的算法,最好用cbegin()和cend()迭代器。

equal算法,只读,用于判断两个序列是否保存相同的值。它比较两个序列中对应的每个元素,如果所有对应元素相等,则返回true,否则返回false。
该算法中,两个元素类型不必一样,只要能用==运算符比较两个元素类型就可以。

equal(seq1.cbegin(),seq1.cend(),seq2.cbegin())
//三个参数,前两个参数表示序列1的元素范围,第三个参数表示第二个序列的首元素
//序列2的元素数目至少和序列1的一样

3、写算法

写算法会把新值赋予给序列中的元素。使用这些算法时,必须确保序列大小不小于算法写入的元素数目。这些算法无法改变容器的大小。
一些算法会向指定范围写入元素,比如fill。

//该算法支持将指定范围内的元素值赋予新值
fill(v.begin(),v.end(),value);
//前两个参数表示指定范围,第三个参数表示想要设置的值

只要这个范围是有效的,那么写入操作就是安全的。
此外,算法不会检查写操作,可能会导致一些错误,比如fill_n。

fill_n(dest,n,val);
//fill_n把值val写入迭代器dest指向的元素开始的n个元素

vector<int>vec;
fill_n(vec.begin(),vec.size(),0);	//把vec中所有元素置为0
fill_n(vec.begin(),10,0);
//错误,vec是一个空vector,结果是未定义的

下面介绍下back_inserter。
之前fill_n会导致错误,因为容器没有足够空间来容纳数据。我们可以使用back_inserter向容器中写入数据,它是定义在头文件iterator中。
back_inserter接受一个指向容器的引用,返回一个与该容器绑定的插入迭代器。当调用此迭代器赋值时,赋值运算符会调用push_back把元素添加到容器中,如下:

vector<int>vec;
auto it=back_inserter(vec);	//通过该迭代器赋值会把元素添加到vec中
*it=42;						//vec中有一个元素42

此外,还可以利用back_inserter来创建一个迭代器,作为算法的参数来使用,如下

vector<int>vec;
fill_n(back_inserter(vec),10,0);//添加了10个值为0的元素到vec中
//正确,back_inserter创建迭代器,可以用来把元素添加到vec中

在每步迭代中,fill_n向vec中一个元素赋值,每次都会调用push_back操作。

用copy算法可以实现序列元素的拷贝。

copy(begin,end,array);
//前两个参数表示指定的输入范围,第三个参数表示目的序列的起始位置
//也就是说将begin到end所表示的元素拷贝到array指向的目的序列
//可以用copy实现内置数组的拷贝
int a1[]={0,1,2,3,4,5,6,7,8,9};
int a2[sizeof(a1)/sizeof(*a1)];//创建一个与a1一样大的数组
auto ret=copy(begin(a1),end(a1),a2);//把a1的内容拷贝到a2
//ret指向拷贝到a2的尾元素之后的位置
//copy返回的是目的位置迭代器增加完成的值。

此外还有replace算法,它将序列中所有等于给定值的元素都改为另一个值。

replace(v.begin(),v.end(),0,42);
//前两个参数指定输入序列范围,第三个参数表示要查找的值,第四个表示新的值

4、重排算法

某些算法会重排容器中元素的顺序,比如sort算法,它会让输入序列的元素变成有序的。

sort(begin(),end(),cmp);
//前两个参数指定要排序的范围,第三个参数可以是自己定义的比较函数

unique算法重排输入序列,将相邻的重复项“消除”,并返回一个指向没有重复值范围的末尾迭代器。

vector<string> words={red,red,red,the,the,blue,slow,quick};
auto end_unique=unique(words.begin(),words.end());//words会变成以下序列
{red,the,blue,slow,quick,???,???,???}//???指代元素,不知道这些元素的值
end_unique迭代器会指向第一个问号所在位置

其中words的大小未改变,还是有10个元素。它只是覆盖相邻的重复元素,使得不重复元素出现在序列开始部分。end_unique迭代指向的元素及后面的元素依旧存在。
可以使用erase成员来实现真正的删除操作。

words.erase(end_unique,word.end());//把最后三个元素全部删除
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值