您的位置:首页 > 编程语言 > C语言/C++

C++:泛型算法基础

2015-05-16 10:49 232 查看

泛型算法

顺序容器只定义了很少的操作,我们希望容器支持更多的操作。C++标准库为了满足更多开发者的需求,实现了一些经典算法的公共接口,因为这些算法不依赖于具体的容器,而是借助迭代器对容器进行操作,所以常称其为泛型算法

泛型算法都定义在
<algorithm>
头文件中,少数数值型算法定义在
<numeric>
头文件中。

一般情况下,算法不会直接操作容器,而是借助容器的迭代器来遍历、操作容器中的元素。而且,大多数算法绝对不会改变容器的长度。(erase和insert等操作不是算法,而是“容器操作”,注意分清)

算法分类

只读算法

只读算法会读取输入范围内的元素,而绝对不会改变元素,这种情况下我们一般使用常量迭代器。

find:

auto it = find(intVec.cbegin(), intVec.cend(), value);
if (it != intVec.cend())
cout << *it << endl;


find查找容器中是否存在值为value的元素,如果不存在返回尾后迭代器。

accumulate:

auto sum = accumulate(intVec.cbegin(), intVec.cend(), 0);


accumulate定义在
<numeric>
头文件中,用来求容器中一段范围的和,第三个参数是sum的初始值,注意初始值的选取和容器中元素的类型是有关系的。如果是double型要初始化为0.0.

值得一提的是,accumulate并不支持所有的元素类型。

元素类型必须定义了“+”操作才可以用accumulate。

例如:

vector<char*> vec ={ "i am" , "you are"};
auto sum = accumulate(vec.cbegin(),vec.cend()," ");


因为char*并不支持‘+’操作,所以编译器报错,如果是
vector<string>
是可以的。

equal:

equal算法用于确定两个序列是否保存相同的值。

equal(c1.cbegin(), c1.cend(), c2.cbegin());


该算法接收三个迭代器。前两个迭代器表示第一个序列的范围,第三个迭代器是第二个序列的起始迭代器位置。这个算法基于这样的编程假设:

1.c1和c2容器类型不必相同,但是容器中的元素类型必须相同

2.容器中的元素类型必须支持“==”操作。(例如某些自定义类可能没有重载“==”运算符,那么就不能用该算法)

3.第二个序列的长度至少和第一个序列的长度相同

写操作算法与迭代器适配器

前边曾经提到过,绝大部分算法不会修改容器的长度,这好像限制了某些操作。

例如:

fill_n:

fill_n算法原型
fill_n(dest,n,val)
向目标位置dest填充n个值为val的元素,dest是一个迭代器。

vector<int> intVec;//空容器
fill_n(intVec.begin(), 10, 0);


这里运行会报错。因为规定算法不可以修改容器的长度,此时容器长度为0。这一点很容易出现致命错误,因为编译器不会检查算法的写操作是否合法,需要程序员保证

我们可以先分配给intVec一些空间后,再指向fill_n操作。

intVec.resize(20);
fill_n(intVec.begin(), 10, 0);


注意这里是resize而不是reserve。vector 的reserve增加了vector的capacity,但是它的size没有改变!而resize改变了vector的capacity同时也增加了它的size!

除了刚才的方法,还可以用一种叫做”插入迭代器”的方式来得到我们期望的结果。

插入迭代器
back_inserter
是一种”迭代器适配器“。

适配器是一种接口转换器,它可以让一种东西看起来像另一种东西。

在这里
back_inserter
使得迭代器能够像函数
push_back
进行一样的操作,它使得一个迭代器看起来像是调用了
push_back
一样。


fill_n(back_inserter(intVec), 10, 0);//添加10个0到intVec中去


这里在提醒一下,我们之前说过算法不可以改变容器的size,那这里为什么可以成功运行不出错呢?

原因在于,算法操作了一个迭代器,迭代器可以完成向容器中添加元素的功能,但是算法本身永远不会这么做,所以和我们之前说的并不矛盾。

auto it1 = back_inserter(intVec);//插入器绑定到了intVec上并且生成了一个迭代器it
*it1 = 10;//相当于intVec.push_back(10)


除了
back_inserter
之外还有
front_inserter
(调用
push_front
)和
inserter
调用容器操作的
inserter


例如:
auto it3 = inserter(intVec, intVec.begin())
在intVec.begin()位置之前调用
insert()
容器操作。

当然,
vector<typename>
并不可以使用
front_inserter()
,因为
vector<typename>
上没有定义
push_front
操作。

copy:

copy向一个目标位置写入一定范围内的数据。

这个算法接受三个迭代器
copy(it1,it2,dest)
表示将[it1,it2)范围内的元素copy到dest为起始位置的序列(dest是一个迭代器).

由于copy是算法不会改变目标容器的长度,所以下述操作是错误的:

vector<int> l;
copy(intVec.cbegin(), intVec.cend(), l.begin());


l是空容器,l.size() == 0,copy不可以改变其size所以操作出错。可以预先改变l的size或者利用
back_inserter


改变l的size:

vector<int> l;
l.resize(100);
copy(intVec.cbegin(), intVec.cend(), l.begin());


利用
back_inserter


vector<int> l;
copy(intVec.cbegin(), intVec.cend(), back_inserter(l));


replace:

replace操作替换某一段区间的值
replace(it1,it2,value,aim_value)
将[it1,it2)区间内的value值替换为aim_value

replace(l.begin(), l.end(), 0, 101);


将l的所有0替换为101.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐