头文件:
algorithm(大多数算法
),numeric(数值泛型算法
).
例:
auto result = find(vec.cbegin(), vec.cend(), val
);
此例中,vec为一个int的vector,我们调用find函数
通过对所给迭代器范围的遍历寻找给定数值val.若找到,它返回第一个等于给定值的元素的迭代器,若无匹配元素,希尔返回第二个参数来表示搜索失败.
一般情况下算法的操作特点:
他们不直接操作容器,而是遍历由两个迭代器指定的一个元素
范围来进行操作.
泛型算法依赖于迭代器之上而不会执行容器操作的特性,
算法操作步骤不依赖于容器所保存的元素类型
算法可能改变容器中保存的值,也可能移动元素,但不可添加或删除元素.
所以永远不会改变底层容器的大小.
一种算法一般可以
通过迭代器操作多种容器
.甚至无需理会保存元素的是不是容器.
初识泛型算法
只读算法:
int sum = accumulate(ve
c.cbegin(), vec.cend(), 0
);
accumulate为求和函数,接受三个参数,前连个指出需要求和的元素范围,第三个参数是和的初始值.此时,前两个参数指定的序列中元素的类型必须与第三个匹配.
因为string支持"+"运算符,所以此函数对string类型适用:
string sum = accumulate(vec.cbegin(), vec.cend(), string(""
)
);
应该注意的,string类型调用此函数时,第三个参数必须是新建的初始化为空的串,如果只将空串当做一个字符串字面值
传递给第三个参数,则用于保存和的对象的类型是const char*,此类型不支持"+"运算符.
另一个只读算法是equal,用于确定两个序列是否保存相同的值.
equal(roster1.cbegin(), roster1.cend(), roster2.cbegin()
);
equal利用迭代器完成操作,所以此函数可以比较不同容器中的元素,而且,元素类型也不必一样,只要能用"==
"来比较两个元素类型即可.equal一般假设第一个序列比第二个短,即第一个中每个元素在第二个中都有对应元素.
操作两个序列的算法之间的区别在于我们如何传递第二个序列.equal接受三个参数,而其他算法接受四个参数.我们在使用equal时应当确保算法不会试图访问第二个序列中不存在的元素.因为如果第一个序列大于第二个系列,那么在比较到第二个末尾后,下一步会开始第二个末尾之后的不存在的元素.
写容器元素的算法
一些算法将新值赋予序列中的元素.此时,应当注意写入的元素数量不能大于容器大小,因为算大不能改变容器大小.例如算法fill:
fill (vec.begin(), vec.begin() + vec.size()/2
, 0
); //将vec中的前一半全部置为10.
fill_n(dest, n, val
); fill_n将dest
迭代器指向的元素开始的指定n个元素之后都置为val.
!注意:只定义但未初始化的空容器上不能进行写操作
介绍back_inserter
头文件:iterator
一种保证算法有足够元
素空间来容纳输出数据的方法是使用插入迭代器.
back_inserter接受一个指向容器的引用,返回一个人与该容器绑定的插入迭代器,当我们使用此迭代器赋值时,赋值运算符会调用push_back将一个具有给定值的元素添加到容器.
vector<int
> vec;
auto it = back_inserter(vec
);
拷贝算法(copy)
此算法接受三个迭代器,前两个表示一个输入范围,第三个表示序列的起始位置.
ret = copy(begin(a1
), end(a1
), a2
); //将a1的内容拷贝给a2
copy返回的是其目的位置迭代器(递增后
)的值,即,ret指向拷贝后的a2尾元素之后的位置.
replace(list.begin(), list.end(), 0, 42
); //将list中的所有0换成42
若此时希望原序列保留,可调用replace_copy.
replace_copy(il
st.c
begin(), ilst
.cend()
, back_inserter(ivec
), 0, 42
);
重排容器元素的算法
某些算法会重排容器中元素的顺序,例如sort,它是利用元素类型的<符来实现的.
the quick red fox jumps over the slow red turtle
首先应该使用sort排序
sort(words
.begin(),
word
s.end()
);
排序结果为:
f
ox jumps over quick red red
slow the
the turtle
然后使用unique , 此函数重拍输入范围,将不重复的单词放在vector的开始部分,重复的放在末尾处,并返回一个指向不重复值范围末尾处的迭代器
然后使用unique , 此函数重拍输入范围,将不重复的单词放在vector的开始部分,重复的放在末尾处,并返回一个指向不重复值范围末尾处的迭代器
auto end_unique = unique(words.begin(), words_end()
);
排序结果为:
fox jumps over quick red
slow the
turtle red the,返回的迭代器end_unique指向turtle之
后
再使用erase删掉重复的:
words.erase(end_unique, words.end()
);
即最后得出排序:
fox jumps over quick red
slow the
turtle
定制操作
向算法传递函数
谓词:是一个可调用的表达式,其返回结果是一个能用作条件的值.标准库算法所使用的谓词分为一元谓词(接受一个参数
)和二元谓词.
例如函数isShorter.
bool isShorter(const string& s1, const string& s2
)
{
return s1.size() < s2.size();
}
sort (words.begin(), words.end(), isShorter
); //按长度由短至长排序words
再探迭代器
除了为每个容器定义的迭代器之外,标准库在头文件iterator中定义了额外的几种迭代器:
插入迭代器:
被
绑定到容器上,用来向容器插入元素,
插入迭代器是一种迭代器适配器,它接受一个容器,生成一个迭代器,能实现向给定容器添加元素.
插入迭代器根据插入位置分为三种:
back_inserter 创建一个使用push_back的迭代器,实现
尾部插入
front_inserter 创建一个使用front_back的迭代器,
实现
头部插入
inserter 创建一个使用insert的迭代器,接受两个参数
流迭代器:
被
绑定到输入或输出流上,用来遍历所关联的IO流.标准库定义istream_iterater读取输入流,ostream_iterater读取输出流,通过使用流迭代器,我们可以用泛型算法从流对象读取数据以及向其写入数据.
istream_iterator<int> in_iter(cin); //将in
_iter绑定到cin上
istream_iterator<int> eof; //eof被定义为空的istream_iterator,此处可以当做
尾后迭代器使用
while(in_iter
!= eof
) {
vec.push_back(*in_iter++
);
}
也可以直接用一对表示元素范围ide迭代器来构造vec.
istream_iterator<int
> in_iter(cin
), eof;
vector<int
> vec(in_iter, eof
);
istream_iterator允许懒惰求值
当我们将一个istream_iterator绑定到一个流时,标准库并不保证迭代器
立即
从流读取迭代器,
直到我们使用迭代器时才真正读取.标准库保证在我们第一次解引用迭代器之前,从流中读取数据的操作已经完成了.特别情况下,立即或推迟读取会影响程序结果:比如创建一个itream_iterator,没有使用就销毁;或我们正从两个不同的对象同步读取同一个流.
ostream_iterator操作
我们可以对任何具有输出运算符的类型定义ostream_iterator,我们为它提供可选的第二参数,是一个字符串,在输出每个元素后都会打印此字符串.
反向迭代器:
反向迭代器就是在容器中从尾元素向首元素反向移动的迭代器,
除了forward_list之外的标准库容器都有反向迭代器.
泛型算法
算法所要求的迭代器操作可以分为五个类别:
输入迭代器,输出迭代器,前向迭代器,双向迭代器,随机访问迭代器