正如STL为我们封装了很多数据结构一样,STL同样也为我们提供了很多通用算法,例如排序,查找等。这些算法本身实际上就是一种函数模板,它不依赖与具体的类型,而是通过迭代器和模板来实现的。对于通用算法,这里有一个重要的概念,那就是:算法绝不执行Container操作,它只会使用迭代器来执行它的逻辑,永远不会直接的去调用容器本身的函数。如果我们为算法提供的是原始迭代器,那么算法就绝不会改变底层容器的大小。
STL提供的算法库很庞大,要记下这么多的算法是很困难的,所幸它几乎所有的算法都遵循如下的结构形式:
alg (beg, end, other parms); alg (beg, end, dest, other parms); alg (beg, end, beg2, other parms); alg (beg, end, beg2, end2, other parms);
要学习STL的算法,我们还是从最基本的查询算法开始,它的声明形式如下:
template <class InputIterator, class T> InputIterator find ( InputIterator first, InputIterator last, const T& value );
对于查找算法,STL提供的还有find_first_of(和string::find_first_of的用法类似),find_if等等。
接下来是说下排序算法,STL所提供的排序算法要求的迭代器类型都必须是随机迭代器(可以参考我的上一篇笔记)。下表列出了STL所提供的排序算法:
函数名 |
功能描述 |
sort |
对给定区间所有元素进行排序 |
stable_sort |
对给定区间所有元素进行稳定排序 |
partial_sort |
对给定区间所有元素部分排序 |
partial_sort_copy |
对给定区间复制并排序 |
nth_element |
找出给定区间的某个位置对应的元素 |
is_sorted |
判断一个区间是否已经排好序 |
partition |
使得符合某个条件的元素放在前面 |
stable_partition |
相对稳定的使得符合某个条件的元素放在前面 |
其中,有stable修饰的函数表示该排序算法是稳定的,即对于相等的元素,不改变它的原始序列。
以sort为例,看一下它的声明形式:template <class RandomAccessIterator> void sort ( RandomAccessIterator first, RandomAccessIterator last ); template <class RandomAccessIterator, class Compare> void sort ( RandomAccessIterator first, RandomAccessIterator last, Compare comp );
然后,我们来关注一下累加算法。先看它的声明形式:
template <class InputIterator, class T> T accumulate ( InputIterator first, InputIterator last, T init ); template <class InputIterator, class T, class BinaryOperation> T accumulate ( InputIterator first, InputIterator last, T init, BinaryOperation binary_op );
同样,根据同样算法的一般形式,前两个参数规定了一个序列范围,这个范围内的元素都将被累加起来,但三个参数指出了累加的初始值。第一个版本需要元素本身支持加法操作,而第二个版本还有第四个参数,这个参数可以传递一个函数对象,它提供两个基本元素的相加操作。需要注意的是,由于该函数模板的实例化是根据第三个参数而来的,因此在传递第三个参数时,我们需要格外小心使它的类型与前两者的元素类型保持一致。下面的例子就是一个容易出错的典范:
//v is a vector<double> accumulate<v.begin(), v.end(), 0);
C语言中有这么一个函数memset,它可以对一片内存区连续的填充值,在STL里也提供了一些类似的函数,不过它们更加抽象,其作用对象时容器,而非实际的物理内存区。这些函数大多以fill开头。需要注意的是,其中一些填充函数的使用并不是很安全,需要程序员自己去保证容器的容量足够大。例如,下面关于对fill_n函数的使用就是不安全的:
vector<int> vec; // empty vector // disaster: attempts to write to 10 (nonexistent) elements in vec fill_n(vec.begin(), 10, 0);
vector<int> vec; // empty vector // ok: back_inserter creates an insert iterator that adds elements to vec fill_n (back_inserter(vec), 10, 0); // appends 10 elements to vec
插入迭代器适配器主要有三种,它们的写操作对应调用的容器操作如下:
back_inserter | push_back |
front_inserter | push_front |
inserter | insert |
下面再简单列举一下常用的算法的声明形式,具体使用请查阅帮助文档。
剔除容器中重复的元素,该函数要求重复元素必须是连续的:
template <class ForwardIterator> ForwardIterator unique ( ForwardIterator first, ForwardIterator last ); template <class ForwardIterator, class BinaryPredicate> ForwardIterator unique ( ForwardIterator first, ForwardIterator last, BinaryPredicate pred );
template <class InputIterator, class OutputIterator> OutputIterator copy ( InputIterator first, InputIterator last, OutputIterator result );
template < class ForwardIterator, class T > void replace ( ForwardIterator first, ForwardIterator last, const T& old_value, const T& new_value );
template < class InputIterator, class OutputIterator, class T > OutputIterator replace_copy ( InputIterator first, InputIterator last, OutputIterator result, const T& old_value, const T& new_value );
template <class InputIterator, class Predicate> typename iterator_traits<InputIterator>::difference_type count_if ( ForwardIterator first, ForwardIterator last, Predicate pred );