定义于SGI<stl_algo.h>内的所有算法,除了set/heap 相关算法之前已经介绍过,其余安排在该文中。其中有很单纯的循环遍历,也有很复杂的快速排序。运作相对单纯者,在第一小节中,其他相对比较复杂的算法,每个算法各一小节。
目录
单纯的数据处理
adjacent_find
count
count_if
find
find_if
find_end
find_first_of
for_each
generate
generate_n
includes(应用于序列区间)
max_element
merge
min_element
partition
remove(移除但不删除)
remove_copy
remove_if
remove_copy_if
replace
replace_copy
replace_if
replace_copy_if
reverse
reverse_copy
rotate
rotate_copy
search
search_n
swap_ranges
transform
unique
unique_copy
单纯的数据处理
这一小节所列的算法,都只进行单纯的数据移动,线性查找,计数,循环遍历,逐一对元素施加指定运算等操作。它们的运作逻辑都相对单纯,直观而易懂。基本通过注释可以明白其含义。
深入源码之前,我们先看看几个函数的示例:此示例包括adjacent_find,count,count_if,find,find_if, find_end,find_first_of,for_each,generate, generate_n,remove,remove_copy,remove_if, remove_copy_if,replace, replace_copy,replace_copy_if, reverse, reverse_copy, rotate, rotate_copy,search, search_n,swap_ranges,transform等函数数。
#include <algorithm>
#include <vector>
#include <functional>
#include <iostream>
using namespace std;template <class T>
struct display {void operator() (const T&x) {cout << x << ' ';}
};struct even {bool operator() (int x) const { return x%2 ? false : true; }
};class event_by_two {
public:int operator()() const { return _x += 2; }
private:static int _x;
};
int event_by_two::_x = 0;int main() {int ia[] = {0,1,2,3,4,5,6,6,6,7,8};vector<int> iv(ia, ia+sizeof(ia)/sizeof(int));cout << *adjacent_find(iv.begin(), iv.end()) << endl; // 6cout << *adjacent_find(iv.begin(), iv.end(), equal_to<int>()) << endl; // 6cout << count(iv.begin(), iv.end(), 6) << endl; // 3 元素=6的个数cout << count_if(iv.begin(), iv.end(), bind2nd(less<int>(), 7)) << endl; // 9 小于7的元素个数cout << *find(iv.begin(), iv.end(), 4) << endl; // 4 找出iv中第一个等于4的位置cout << *find_if(iv.begin(), iv.end(), bind2nd(greater<int>(), 2)) << endl; // 3 找出iv中第一个大于2的元素的位置vector<int> iv2(ia + 6, ia+8); // {6, 6}auto iter = find_end(iv.begin(), iv.end(), iv2.begin(), iv2.end()); // 从后往前找,找到匹配序列的起始位置cout << *iter << ", " << iter - iv.begin() << endl; // 6, 7iter = find_first_of(iv.begin(), iv.end(), iv2.begin(), iv2.end()); // 从前往后找,找到第一个匹配序列的起始位置cout << *iter << ", " << iter - iv.begin() << endl; // 6, 6for_each(iv.begin(), iv.end(), display<int>()); // 0 1 2 3 4 5 6 6 6 7 8cout << endl;generate(iv2.begin(), iv2.end(), event_by_two());for_each(iv2.begin(), iv2.end(), display<int>()); // 2 4cout << endl;generate_n(iv.begin(), 3, event_by_two());for_each(iv.begin(), iv.end(), display<int>()); // 6 8 10 3 4 5 6 6 6 7 8cout << endl;remove(iv.begin(), iv.end(), 6); // 删除所有=6的元素,实际上是把后面的元素把=6的元素进行了覆盖,但是迭代器还维持了原来的大小for_each(iv.begin(), iv.end(), display<int>()); // 6 8 10 3 4 5 7 8 6 7 8cout << endl;vector<int> iv3(12);remove_copy(iv.begin(), iv.end(), iv3.begin(), 6);for_each(iv3.begin(), iv3.end(), display<int>()); // 8 10 3 4 5 7 8 7 8 0 0 0cout << endl;// 删除小于6的元素,尾部可能存在残存元素remove_if(iv.begin(), iv.end(), bind2nd(less<int>(), 6));for_each(iv.begin(), iv.end(), display<int>()); // 8 10 7 8 6 6 7 8 6 7 8cout << endl;// 去除小于7的元素,剩余的copy到新的容器中,容器尾部可能有残余数据remove_copy_if(iv.begin(), iv.end(), iv3.begin(), bind2nd(less<int>(), 7));for_each(iv3.begin(), iv3.end(), display<int>()); // 8 10 7 8 7 8 7 8 8 0 0 0 cout << endl;// 替换,将所有元素值为6的元素,替换为3replace(iv.begin(), iv.end(), 6, 3);for_each(iv.begin(), iv.end(), display<int>());// 8 10 7 8 3 3 7 8 3 7 8cout << endl;// 将所有元素值为3的元素,替换为5,并拷贝到新的容器iv3replace_copy(iv.begin(), iv.end(), iv3.begin(), 3, 5);for_each(iv3.begin(), iv3.end(), display<int>());// 8 10 7 8 5 5 7 8 5 7 8 0cout << endl;// 对满足条件的元素进行替换replace_if(iv.begin(), iv.end(), bind2nd(less<int>(), 5), 2);for_each(iv.begin(), iv.end(), display<int>());// 8 10 7 8 2 2 7 8 2 7 8 cout << endl;// 将满足条件的元素值进行替换,并拷贝到新的容器replace_copy_if(iv.begin(), iv.end(), iv3.begin(), bind2nd(less<int>(), 8), 9);for_each(iv3.begin(), iv3.end(), display<int>());// 8 10 9 8 9 9 9 8 9 9 8 0cout << endl;// 对区间内的元素进行逆序reverse(iv.begin(), iv.end());for_each(iv.begin(), iv.end(), display<int>());// 8 7 2 8 7 2 2 8 7 10 8cout << endl;// 对区间进行逆序,并将逆序的结果拷贝到新的区间reverse_copy(iv.begin(), iv.end(), iv3.begin());for_each(iv3.begin(), iv3.end(), display<int>());// 8 10 7 8 2 2 7 8 2 7 8 0 cout << endl;// 旋转(互换元素)[first, middle)和[middle, last)rotate(iv.begin(), iv.begin() + 4, iv.end());for_each(iv.begin(), iv.end(), display<int>());// 7 2 2 8 7 10 8 8 7 2 8cout << endl;// 旋转(互换元素)[first, middle)和[middle, last), 结果至于新区间rotate_copy(iv.begin(), iv.begin() + 5, iv.end(), iv3.begin());for_each(iv3.begin(), iv3.end(), display<int>());// 10 8 8 7 2 8 7 2 2 8 7 0 cout << endl;// 查找某个子序列的第一次出现的地点int ia2[2] = {2, 8};vector<int> iv4(ia2, ia2+2);iter = search(iv.begin(), iv.end(), iv4.begin(), iv4.end());cout << *iter << ", " << iter - iv.begin() << endl; // 2, 2// 查找连续出现2个8的序列的起点iter = search_n(iv.begin(), iv.end(), 2, 8);cout << *iter << ", " << iter - iv.begin() << endl; // 8, 6// 查找连续出现3个小于8的序列的起点iter = search_n(iv.begin(), iv.end(), 3, 8, less<int>());cout << *iter << ", " << iter - iv.begin() << endl; // 7, 0// 将两个区间内的元素互换,第二区间的元素个数不应小于第一区间的元素个数(将元素数目小的区间放前面)swap_ranges(iv4.begin(), iv4.end(), iv.begin());for_each(iv.begin(), iv.end(), display<int>());// 2 8 2 8 7 10 8 8 7 2 8cout << endl;for_each(iv4.begin(), iv4.end(), display<int>());// 7 2cout << endl;// 修改区间的值,全部减2transform(iv.begin(), iv.end(), iv.begin(), bind2nd(minus<int>(), 2));for_each(iv.begin(), iv.end(), display<int>());// 0 6 0 6 5 8 6 6 5 0 6cout << endl;// 修改区间的值,令第二区间的元素的值加到第一区间对应的元素身上,并将结果写入第三区间transform(iv.begin(), iv.end(), iv.begin(), iv.begin(), plus<int>());for_each(iv.begin(), iv.end(), display<int>());// 0 12 0 12 10 16 12 12 10 0 12cout << endl;return 0;
}
以下示例包含函数max_element, merge, partition,unique, unique_copy.
#include <algorithm>
#include <vector>
#include <functional>
#include <iostream>
using namespace std;template <class T>
struct display {void operator() (const T&x) {cout << x << ' ';}
};struct even {bool operator() (int x) const { return x%2 ? false : true; }
};class event_by_two {
public:int operator()() const { return _x += 2; }
private:static int _x;
};
int event_by_two::_x = 0;int main() {int ia[] = {0,1,2,3,4,5,6,6,6,7,8};vector<int> iv5(ia, ia+sizeof(ia)/sizeof(int));vector<int> iv6(ia+4, ia+8);vector<int> iv7(15);for_each(iv5.begin(), iv5.end(), display<int>()); // 0 1 2 3 4 5 6 6 6 7 8 cout << endl;for_each(iv6.begin(), iv6.end(), display<int>()); // 4 5 6 6 cout << endl;cout << *max_element(iv5.begin(), iv5.end()) << endl; // 8cout << *min_element(iv5.begin(), iv5.end()) << endl; // 0// 判断iv6的所有元素是否都在iv5中,该函数需保证iv5及iv6的元素是经过排序的cout << includes(iv5.begin(), iv5.end(), iv6.begin(), iv6.end()) << endl; // 1// 将两个序列合并,该函数需保证输入序列式sorted rangesmerge(iv5.begin(), iv5.end(), iv6.begin(), iv6.end(), iv7.begin());for_each(iv7.begin(), iv7.end(), display<int>()); // 0 1 2 3 4 4 5 5 6 6 6 6 6 7 8 cout << endl;// 符合条件的元素放在容器的前端,不符合的元素放在后段;不保留原相对次序partition(iv7.begin(), iv7.end(), even());for_each(iv7.begin(), iv7.end(), display<int>()); // 0 8 2 6 4 4 6 6 6 6 5 5 3 7 1cout << endl;// 去除“连续重复”的元素,去除后,可能会有残余数据unique(iv5.begin(), iv5.end());for_each(iv5.begin(), iv5.end(), display<int>()); // 0 1 2 3 4 5 6 7 8 7 8 cout << endl;// 去除“连续重复”的元素,将结果拷贝到新的区间unique_copy(iv5.begin(), iv5.end(), iv7.begin());for_each(iv7.begin(), iv7.end(), display<int>()); // 0 1 2 3 4 5 6 7 8 7 8 5 3 7 1 cout << endl;return 0;
}
adjacent_find
找到第一组满足条件的相邻元素。这里所谓的条件,在版本一中是指“两元素相等”,在版本二中允许用户指定一个二元运算,两个操作数分别是相邻的第一个元素和第二个元素。
// 版本一
template<class ForwardIterator>
ForwardIterator adjacent_find(ForwardIterator first, ForwardIterator end) {if (first == last) return last;ForwardIterator next = first;while(++next != last) {if (*first == *next) return first;first = next;}return last;
}// 版本二
template<class ForwardIterator, class BinaryPredicate>
ForwardIterator adjacent_find(ForwardIterator first, ForwardIterator end, BinaryPredicate binary_pred) {if (first == last) return last;ForwardIterator next = first;while(++next != last) {if (binary_pred(*first, *next)) return first;first = next;}return last;
}
count
运用equality操作符,将[first,last)区间内的每一个元素拿来和指定值value比较,并返回与value相等的元素个数。
template<class InputIterator, class T>
typename iterator_traits<InputIterator>::difference_type count(InputIterator first, InputIterator last, const T& value) {typename iterator_traits<InputIterator>::difference_type n = 0;for (; first != last; ++first) if (*first == value) ++n;return n;
}
请注意,count()有一个早起版本,规格如下。它和上述版本的主要差异是,计数器由参数提供:
template<class InputIterator, class T>
void count(InputIterator first, InputIterator last, const T& value, Size&n) {for (; first != last; ++first) if (*first == value) ++n;
}
count_if
将指定操作(一个仿函数)pred实施与[first,last)区间内的每一个元素身上,并将"造成pred之计算结果为true"的所有元素的个数返回。
template<class InputIterator, class Predicate>
typename iterator_traits<InputIterator>::difference_type count_if(InputIterator first, InputIterator last, Predicate pred) {typename iterator_traits<InputIterator>::difference_type n = 0;for (;first != last;++first) if (pred(*first)) ++n;return n;
}
count_if的早起版本同之前的count早起版本,计数结果由参数传入:
template<class InputIterator, class Predicate>
void count_if(InputIterator first, InputIterator last, Predicate pred, Size&n) {for (;first != last;++first) if (pred(*first)) ++n;
}
find
根据equal操作符,循序查找[first,last)内的所有元素,找出第一个匹配"等同equality条件"者。如果找到,就返回一个InputIterator指向该元素,否则返回last。
template <class InputIterator, class T>
InputIterator find(InputIterator first, InputIterator last, const T& value) {while(first != last && *first != value) ++first;return first;
}
find_if
根据指定的pred运算条件(以仿函数表示),循序查找[first,last)内的所有元素,找出第一个pred运算结果为true者。如果找到就返回InputIterator指向该元素,否则返回迭代器last。
template <class InputIterator, class Predicate>
InputIterator find(InputIterator first, InputIterator last, Predicate pred) {while(first != last && !pred(*first)) ++first;return first;
}
find_end
在序列一[first1,last1)所涵盖的区间中,查找序列二[first2,last2)的最后一次出现点。如果序列一之内不存在“完全匹配序列二”的子序列。便返回迭代器last1.此算法有两个版本,版本一使用元素型别所提供的equality操作符,版本二允许用户指定某个二元运算,作为判断元素相等与否的依据。
由于这个算法查找的是“最后一次出现地点”,如果我们有能力逆向查找,题目就变成了“首次出现地点”,那对设计者而言比较省力。逆向查找的关键在于迭代器的双向移动能力,因此SGI将算法设计为双层结构,一般称呼此种上层函数为dispatch function(分派函数、派送函数):
template<class ForwardInputIterator1, class ForwardInputIterator2>
inline ForwardInputIterator1 find_end(ForwardInputIterator1 first1, ForwardInputIterator1 last1,ForwardInputIterator2 first2, ForwardInputIterator2 last2) {typedef typename iterator_traits<ForwardIterator1>::iterator_category category1;typedef typename iterator_traits<ForwardIterator2>::iterator_category category2;return __find_end(first1, last1, first2, last2, category1(), category2());
}
这是一种常见的技巧,令函数传递调用过程中产生迭代器类型(iterator category)的临时对象,在利用编译器的参数推导机制(argument deductiion),自动调用某个对应函数。此例对应函数有两个候选者:
template <class ForwardIterator1, class ForwardIterator2>
ForwardIterator1 __find_end(ForwardIterator1 first1, ForwardIterator1 last1ForwardIterator2 first2, ForwardIterator2 last2,forward_iterator_tag, forward_iterator_tag) {if (first2 == last2) return last1;else {ForwardIterator1 result = last1;while(1) {// 调用search函数从头开始找ForwardIterator1 new_result = search(first1, last1, first2, last2);if (new_result == last1) // 没找到return resultelse {result = new_result;first1 = new_result;++first1; // 找到后,区间定位到当前查找到位置的下一个未知} }}
}
以下是双向迭代器的查找方法
template <class BidirectionalIterator1, class BidrectionalIterator2>
BidirectionalIterator1 __find_end(BidirectionalIterator1 first1, BidirectionalIterator1 last1,BidrectionalIterator2 first2, BidrectionalIterator2 last2,bidirectional_iterator_tag, bidirectional_iterator_tag) {// 由于超找的是最后出现地点,因此反向查找可以快速获取结果typedef reverse_iterator<BidirectionalIterator1> reviter1;typedef reverse_iterator<BidirectionalIterator2> reviter2;reviter1 rlast1(first1);reviter2 rlast2(first2);reviter1 result = search(reviter1(last1), rlast1,reviter2(last2), rlast2);if (rrresult == rlast1) // 没找到return last1else {BidirectionalIterator1 result - rrresult.base();advance(result, -distance(first2, last2)); // 调整回到子序列的起始处return result;}
}
为什么最后要将逆向迭代器转回正向迭代器,而不直接移动逆向迭代器呢?因为正向迭代器和逆向迭代器之间有奇妙的“实体关系”和“逻辑关系”。后续会做详细介绍。
find_first_of
本算法以[first2,last2)区间内的某些元素作为查找目标,寻找它们在[first1,last1)区间内第一次出现的地点。举个例子,假设我们希望找出字符串序列synesthesia的第一个元音,我们可以定义第二序列为aeiou。此算法会返回一个ForwardIterator,指向元音序列中任一元素首次出现于第一序列的任何元素,返回的将是last1。本算法的第一个版本使用元素型别提供的equality操作符,第二个版本允许用户指定二元运算符pred。
// 版本一
template <class InputIterator, class ForwadIterator>
InputIterator find_first_of(InputIterator first1, InputIterator last1,ForwadIterator first2, ForwadIterator last2) {for (; first1 != last1; ++first1) for (ForwadIterator iter = first2; iter != last2; ++iter) if (*first1 == *iter) return first1return last1;
}// 版本二
template <class InputIterator, class ForwadIterator, class BinaryPredicate>
InputIterator find_first_of(InputIterator first1, InputIterator last1,ForwadIterator first2, ForwadIterator last2,BinaryPredicate comp) {for (; first1 != last1; ++first1) for (ForwadIterator iter = first2; iter != last2; ++iter) if (comp(*first1, *iter)) return first1return last1;
}
for_each
将仿函数f施行于[first,last)区间内的每一个元素身上。f不可以改变元素内容,以为first和last都是InputIterators,不保证接受赋值行为(assigment),如果需要修改元素内容,应该使用算法transform(),f可以返回一个值,但该值会被忽略。
template <class InputIterator, class Function>
Function for_each(InputIterator first, InputIterator last, Function f) {for(;first != last;++first)f(*first);return f;
}
generate
将仿函数gen的运算结果填写在[first,last)区间内的所有元素身上。所谓填写,有的是迭代器所指元素之assignment操作符。
template <class ForwardIterator, class Generator>
void generator(ForwardIterator first, ForwardIterator last, Generator gen) {for (;first != last; ++first) *first = gen();
}
generate_n
将仿函数gen的运算结果填写在迭代器first开始的n个元素身上。所谓填写,用的是迭代器所指元素的assignment操作符。
template<OutputIterator, class Size, class Generator>
OutputIterator generate_n(OutputIterator first, Size n, Generator n) {for(; n>0; --n, ++first)*first = gen()return first;
}
includes(应用于序列区间)
判断序列二S2是否“涵盖于”序列S1。S1和S2都必须是有序集合,其中的元素都可重复(不必唯一)。所谓涵盖,意思是“S2的每一个元素都出现在S1”。由于判断两个元素是否相等,必须一less或generater运算为依据,因此配合着两个序列S1和S2的排序方式(递增或递减),includes算法可供用户选择采用less或greater进行两个元素的大小比较。
换句话说,如果S1和S2是递增排序(以operator<执行比较操作),includes算法应该这么使用:
includes(S1.begin(), S1.end(), S2.begin(), S2.end());这和includes(S1.begin(), S1.end(), S2.begin(), S2.end(), less<T>());语义相同;这里的T是序列元素的类型;(此处和书中描述略有差异,书中写的是less<int>(),感觉上书中此处描述有待商榷)
如果S1和S2是递减排序(以operator>执行比较操作),includes应该这么使用
includes(S1.begin(),S1.end(), S2.begin(), S2.end(), generator<T>());
注意,S1或S2内的元素都可重复,这种情况下所谓"S1内含S2子集合"的定义是:假设某元素再S2中出现n次,在S1中出现m次,如果m<n,此算法会返回false。
下面是includes算法的两个版本的源码:
// 版本一
template <class InputIterator1, class InputIterator2>
bool includes(InputIterator1 first1, InputIterator1 last1,InputIterator2 first2, InputIterator2 last2) {while(first1 != last1 && first2 != last2) {if (*first2 < *first1) return false;else if (*first1 < *first2) ++first1;else { // *first1 == *first2++first1, ++first2;}}return first2 == last2;
}// 版本二
template <class InputIterator1, class InputIterator2, class Compare>
bool includes(InputIterator1 first1, InputIterator1 last1,InputIterator2 first2, InputIterator2 last2,Compare comp) {while(first1 != last1 && first2 != last2) {if (comp(*first2, *first1)) return false;else if (comp(*first1, *first2)) ++first1;else { // *first1 == *first2++first1, ++first2;}}return first2 == last2;
}
从版本二可知,如果传入一个二元运算comp,却不能使一下的case3代表“两元素相等”
if (comp(*first2, *first1)) ... // case 1
else if (comp(*first1, *first2)) ... // case 2
else // case 3
这个comp将会造成整个includes算法语义错误。同事请注意Compare,即不是BinaryPredicate,也不是BinaryOperation,所以并非随便一个二元运算就可拿来作为comp参数。从这里我们得到一个教训,虽然从语法上来说Compare只是一个template参数(从这个观点看,它叫什么名称都一样),但它(乃至整个STL的符号命名)有深刻含义。即该Compare函数需满足偏序关系。
max_element
这个算法返回一个迭代器,指向序列之中数值最大的元素。其工作原理至为简单。下面是源码:
//版本一
template <class ForwardIterator>
ForwardIterator max_element(ForwardIterator first, ForwardIterator last) {if (first == last) return first;ForwardIterator result = first;while(++first != last) {if (*result < *first) result = first;}return result;
}
//版本二
template <class ForwardIterator, class Compare>
ForwardIterator max_element(ForwardIterator first, ForwardIterator last, Compare comp) {if (first == last) return first;ForwardIterator result = first;while(++first != last) {if (comp(*result, *first)) result = first;}return result;
}
merge
将两个经过排序的序列S1和S2,合并起来并置于新的序列。所得结果也是一个有序序列。返回一个迭代器,指向最后结果序列的最后一个元素的下一个位置。下面是merge算法的两个版本源码:
//版本一
template<class InputIterator1, class InputIterator2, class OutputIterator>
OutputIterator merge(InputIterator1 first1, InputIterator1 last1,InputIterator2 first2, InputIterator2 last2,OutputIterator result) {while(first1 != last1 && first2 != last2) {if (*first2 < *first1) {*result = *first2;++first2;} else {*result = *first1;++first1;}++result;}// [first1,last1),[first2,last2)至多有一个非空序列return copy(first2, last2, copy(first1, last1, result));
}//版本二
template<class InputIterator1, class InputIterator2, class OutputIterator, class Compare>
OutputIterator merge(InputIterator1 first1, InputIterator1 last1,InputIterator2 first2, InputIterator2 last2,OutputIterator result, Compare comp) {while(first1 != last1 && first2 != last2) {if (comp(*first2 , *first1)) {*result = *first2;++first2;} else {*result = *first1;++first1;}++result;}return copy(first2, last2, copy(first1, last1, result));
}
min_element
这个算法返回一个迭代器,指向序列之中数值做小的元素。下面是源码:
//版本一
template <class ForwardIterator>
ForwardIterator min_element(ForwardIterator first, ForwardIterator last) {if (first == last) return first;ForwardIterator result = first;while(++first != last) {if (*first < *result) result = first;}return result;
}//版本二
template <class ForwardIterator, class Compare>
ForwardIterator min_element(ForwardIterator first, ForwardIterator last, Compare comp) {if (first == last) return first;ForwardIterator result = first;while(++first != last) {if (comp(*first, *result)) result = first;}return result;
}
partition
partition会将区间[first, last)中的元素重新排列。所有被一元条件运算pred判定为true的元素,都会被放到区间的前段,被判断为false的元素都会被放在区间的后段。这个算法并不保留元素的原始相对位置。如果需要保留原始相对位置,应使用stable_partion.
下面是partition算法的源代码
template<class BidirectionIterator, class Predicate>
BidirectionIterator partition(BidirectionIterator first,BidirectionIterator last, Predicate pred) {while(true) {while(true) {if (first == last) {return first;}else if (pred(*first)) ++first;else break;}--last;while(true) if (first == last) return first;else if (!pred(*last) --last;else break;iter_swap(first, last);++first;}
}
remove(移除但不删除)
移除[first,last)之中所有与value相等的元素。这一算法并不真正从容器中删除元素(换句话说容器大小并未改变),而是将每一个不与value相等的元素轮番复制给first之后的空间。返回值ForwardIterator标示出重新整理后的最后一个元素的位置。例如序列{0,1,0,2,0,3,0,4},如果执行remove(),希望移除的元素为0,执行结果将是{1,2,3,4,0,3,0,4}.每一个与0不相等的元素,1,2,3,4分贝被拷贝到第一、二、三、四给位置上。第四个位置以后不动。换句话说第四个位置之后是这一算法的残余数据。可将返回的迭代器交给区间所在之容器的erase()member function.注意array不适合使用remove和remove_if(),因为array无法缩小尺寸,导致残余数据永远存在。对array而言,较受欢迎的算法是remove_copy()和remove_copy_if()
template <class ForwardIterator, class T>
ForwardIterator remove(ForwardIterator first, ForwardIterator last, const T& value) {first = find(first, last, value);ForwardIterator next = first;return first == last ? first : remove_copy(++next, last, first, value);
}
此算法需要用到remove_copy,稍后会介绍其实现内容。
remove_copy
移除[first,last)区间内所有与value相等的元素。它并不真正从容器中删除那些元素(换句话说,原容器可能没有任何改变,如果result不是自身迭代器的话),而是将结果复制到了一个以result标示起始位置的容器身上。新容器可以和原容器重叠,但如果对新容器实际给值时,超越了该容器的大小,会产生无法预期的结果。返回值OutputIterator指出该复制的最后一个元素的下一位置。
template <class InputIterator, class OutputIterator, class T>
OutputIterator remove_copy(ForwardIterator first, ForwardIterator last, const T& value) {for(; first != last; ++first) if (*first != value) {*result = *first;++result;} return result;
}
remove_if
移除[first,last)区间内所有被仿函数pred核定为true的元素,它并不真正从容器中删除那些元素。每一个不符合pred条件的元素都会轮番复制给first之后的空间。返回值ForwardIterator标示出重新整理后的最后元素的下一位置。此算法会留有残余数据,如果要删除那些残余数据,可将返回迭代器交给区间所在容器的erase()member function.。代码如下:
template<class ForwardIterator, class Predicate>
ForwardIterator remove_if(ForwardIterator first, ForwardIterator last, Predicate pred) {first = find_if(first, last, pred);ForwarIterator next = first;return first == last ? first : remove_copy_if(++next, last, first, pred);
}
remove_copy_if
移除[first,last)区间内所有被仿函数pred评估为true的元素。它并不真正从容器中删除那些元素,而是将结果复制到一个以resutl标示为起始位置的容器上。新容器可以和原容器重叠,如果新容器容量不足以容纳拷贝过来的元素,会产生无法预期的结果。(所以新容器必须保证有足够空间)。返回值OutputIterator指出被复制的最后元素的下一个位置。
template <class InputIterator, class OutputIterator, class T, class Predicate>
OutputIterator remove_copy_if(ForwardIterator first, ForwardIterator last, const T& value, Predicate pred) {for(; first != last; ++first) if (!pred(*first, value)) {*result = *first;++result;} return result;
}
replace
将[first,last)区间内的所有old_value都以new_value取代。
tempalte <class ForwarIterator, class T>
void replace(ForwarIterator first, ForwarIterator last, const T& old_value, const T & new_value) {for (; first != last; ++first) if (*first == old_value) *first = new_value;
}
replace_copy
行为与replace()类似,唯一不同的是新序列会被复制到result所指的容器中,返回值OutputIterator指向复制的最后一个元素的下一个位置。原序列不发生变化。
template<class InputIterator, class OutputIterator, class T>
OutputIterator replace_copy(InputIterator first, InputIterator last, OutputIterator result, const T& old_value, const T& new_value) {for (; first != last; ++first, ++last) {*result = (*first == old_value) ? new_value : *first;}return result;
}
replace_if
将[first,last)区间内所有“被pred评估为true”的元素,都以new_value取代:
tempalte <class ForwarIterator, class Predicate, class T>
void replace(ForwarIterator first, ForwarIterator last, Predicate pred, const T & new_value) {for (; first != last; ++first) if (pred(*first)) *first = new_value;
}
replace_copy_if
行为与replace_copy类似,但是新序列会被复制到result所指的区间内。OutputIterator指向复制的最后一个元素的下一个位置。
template<class InputIterator, class OutputIterator, class Predicate, class T>
OutputIterator replace_copy(InputIterator first, InputIterator last, OutputIterator result, Predicate pred, const T& new_value) {for (; first != last; ++first, ++last) {*result = pred(*first) ? new_value : *first;}return result;
}
reverse
将序列[first,last)的元素在原容器中颠倒排序。例如序列{0,1,1,3,5}颠倒排序为{5,3,1,1,0}.迭代器的双向或随机定位能力,影响了算法的效率,所有设计为双层架构:
template<class BidrectionalIterator>
inline void reverse(BidrectionalIterator first, BidrectionalIterator last) {__reverse(first, last, iterator_category(first));
}template <class BidirectionalIterator>
void __reverse(BidirectionalIterator first, BidirectionalIterator last, bidirectional_iterator_tag) {while(true)if (first == last || first == --last) return ;else iter_swap(first++, last);
}template <class RandomAccessIterator>
void __reverse(RandomAccessIterator first, RandomAccessIterator last, random_access_iterator_tag) {while (first < last) iter_swap(first++, --last);
}
reverse_copy
行为类似于reverse(), 但产生出来的新序列会被置于以result指出的容器中。返回值OutputIterator执行新产生的最后一个元素的下一个位置。
template<class BidrectionalIterator, class OutputIterator>
OutputIterator reverse_copy(BidrectionalIterator first, BidrectionalIterator last, OutputIterator result) {while (first != last) {--last;*result = *last;++ result;}return result;
}
rotate
将[first,middle)内的元素和[middle,last)内的元素互换。middle所指的元素会成为容器的第一个元素。如果有个数字序列{1,2,3,4,5,6,7},对元素3做旋转操作,会形成{3,4,5,6,7,1,2}.看起来和swap_ragnes功能颇为相似,但是swap_rangs只能交换两个长度相同的区间,rotate()可交换两个长度不同的区间。
根据迭代器的不同,该算法使用了双层架构。
template<class ForwardIterator>
inline void rotate(ForwardIterator first, ForwardIterator middle, ForwardIterator last) {if (first == middle || middle == last) return;__rotate(first, middle, last, distance_type(first), iterator_category(first));
}template<class ForwardIterator, class Distance>
void __rotate(ForwardIterator first, ForwardIterator middle, ForwardIterator last, Distance *, forward_iterator_tag) {for (ForwardIterator I = middle;;) {iter_swap(first, I);++first;++I;if (first == middle) { // 前段结束了if (I == last) return; // 如果后段也结束了,就returnmiddle = I; // 否则调整} else if (I == last) I = middle;}
}template<class BidrectionalIterator, class Distance>
void __rotate(BidrectionalIterator first, BidrectionalIterator middle, BidrectionalIterator last, Distance *, bidrectional_iterator_tag) {reverse(first, middle);reverse(middle, last);reverse(first, last); // 经过三次翻转,满足算法要求
}
template<class RandomAccessIterator, class Distance>
void __rotate(RandomAccessIterator first, RandomAccessIterator middle, RandomAccessIterator last, Distance *, random_access_iterator_tag) {Distance n = __gcd(last - first, middle - first);while(n--) __rotate_cycle(first, last, first + n, middle-first, value_type(first));
}// 最大公因子,利用辗转相除法
template <class EuclideanRingElement>
EuclideanRingElement __gcd(EuclideanRingElement m, EuclideanRingElement n) {while (n != 0) {EuclideanRingElement t = m % n;m = n;n = t;}return m;
}template <class RandomAccessIterator, class Distance, class T>
void __rotate_cycle(RandomAccessIterator first, RandomAccessIterator last, RandomAccessIterator initial, Distance shift, T*) {T value = *initial;RandomAccessIterator ptr1 = initial;RandomAccessIterator ptr2 = ptr1 + shift;while(ptr2 != initial) {*ptr1 = *ptr2;ptr1 = ptr2;if (last - ptr2 > shift)ptr2 += shift;else ptr2 = first + (shift - (last - ptr2))}*ptr1 = value;
}
RandomAccessIterator对应的rotate版本看上去利用了数学最大公约数,以及旋转的一些步骤,算法不是显而易见的;待后续用单独的一篇文章进行说明。
rotate_copy
行为类似rotate(),但是产生出来的新序列会被置于result所指出的容器中。返回OutputIterator指向新产生元素的下一个位置。
由于他不需要就地(in-place)在容器中调整内容,实现上就简单得多。旋转操作起始只是两段元素彼此交换,所以只要先把后段复制到新容器的前端,再把前段接续复制到新容器即可。
template<class ForwardIterator, class OutputIterator>
OutputIterator rotate_copy(ForwardIteratorn first, ForwardIterator middle, ForwardIterator last, OutputIterator result) {return copy(first, middle, copy(middle, last, result));
}
search
在序列一[first1,last1)所涵盖的区间中,查找序列二[first2,last2)的首次出现点。如果序列一内不存在于序列二完全匹配的子序列,便返回迭代器last1.版本一使用元素型别所提供的equality操作符,版本二允许用户指定某个二元运算,作为判断相等的依据。以下只给出了版本一的代码:
template<class ForwardIterator1, class ForwardIterator2>
inline ForwardIterator1 search(ForwardIterator1 first1,ForwardIterator1 last1,ForwardIterator2 first2,ForwardIterator2 last2) {return __search(first1, last1, first2, last2, distance_type(first1), distance_type(first2));
}template<class ForwardIterator1, class ForwardIterator2, class Distance1, class Distance2>
ForwardIterator1 __search(ForwardIterator1 first1,ForwardIterator1 last1,ForwardIterator2 first2,ForwardIterator2 last2Distance1*, Distance2*) {Distance d1 = 0;distance(first1, last1, d1);Distance d2 = 0;distance(first2, last2, d2); if (d1 < d2) return last1;ForwardIterator1 current1 = first1;ForwardIterator2 current2 = first2;while(current2 != last2)if (*current1 == *current2) {++current1;++current2;} else {if (d1 == d2) return last1;else {current1 = ++ first1;current2 = first2;--d1; // 回到S2的起点从头再来}}return first1;
}
search_n
在序列[first,last)所涵盖的区间中,查找"连续count个符合条件之元素"所形成的子序列,并返回一个迭代器指向该子序列的起始处。如果找不到这样的子序列,就返回迭代器last。上述所谓的某条件,在search_n版本之的是相等条件equality,在search_n版本二指的是用户指定的某个二元运算。
例如,面对序列{10, 8,8,7,2,8,7,2,2,,8,7,0},查找“连续两个8”所形成子序列起点,可以这么写
iter1 = search_n(iv.begin(),iv.end(), 2, 8);
查找连续三个小于8的元素,所形成的子序列的起点,可以这么写
iter2=search_n(iv.begin(),iv.end(),3,8,less<int>());
下面是search_n的源码:
// 版本一
template<class ForwardIterator, class Integer, class T>
ForwardIterator search_n(ForwardIterator first, ForwardIterator last, Integer count, const T& value) {if (count <= 0) return first;else {first = find(first, last, value);while(first != last) {Integer n = count - 1;ForwardIterator I = first;++I;while(I != last && n!=0 && *I == value) {++I;--n;}if (n==0) return first;else first = find(I, last, value);}return last;}}// 版本二
template<class ForwardIterator, class Integer, class T, class BinaryPredicate>
ForwardIterator search_n(ForwardIterator first, ForwardIterator last, Integer count, const T& value, BinaryPredicate binary_pred) {if (count <= 0) return first;else {while(first != last) {if (binary_pred(*first, value)) break;++first;}while(first != last) {Integer n = count - 1;ForwardIterator I = first;++I;while(I != last && n!=0 && binary_pred(*I, value)) {++I;--n;}if (n==0) return first;else {while(I != last) {if (binary_pred(*I, value)) break;++I;}first = I;}}return last;}}
swap_ranges
将[first1,last1)区间内的元素与“从first2开始,个数相同”的元素互相交换,代码如下:
template<class ForwardIterator1, class ForwardIterator2>
ForwardIterator2 swap_ragnes(ForwardIterator1 first1,ForwardIterator1 last1,ForwardIterator2 first2) {for (; first1 != last1; ++first1, ++first2) {iter_swap(first1, first2);}return first2;
}
transform
transform()的第一个版本一仿函数op作用于[first,last)中的每一个元素身上,并以其结果产生出一个新的序列。第二个版本以仿函数binary_op作用于一双元素身上,并以其结果产生出一个新的序列。如果第二个序列的元素少于第一个序列,执行结果未可预期。
transform()两个版本都把执行结果放进迭代器result所示的容器中。result也可以指向源端容器,那么transform()的运算结果就会取代该容器内的元素。返回值OutputIterator将指向结果序列的最后元素的下一位置。
// 版本一
template<class InputIterator, class OutputIterator, class UnaryOperation>
OutputIterator transform(InputIterator first, InputIterator last, OutputIterator result, UnaryOperation op) {for(; first != last; ++first,++result) *result = op(*first);return result;
}// 版本二
template<class InputIterator1, class InputIterator2, class OutputIterator, class BinaryOperation>
OutputIterator transform(InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, OutputIterator result, BinaryOperation binary_op) {for(; first1 != last1; ++first1, ++first2,++result) *result = binary_op(*first1, *first2);return result;
}
unique
算法unique()能移除(remove)重复的元素。每当在[first,last)内遇到有重复元素群,它便移除该元素群中第一个以后的所有元素。注意,unique只移除相邻的重复元素,如果想移除所有(包括不相邻的)重复元素,必须先将序列排序,是所有重复元素相邻。
unique会返回一个迭代器指向新区间的尾端,新区间之内不包含相邻的重复元素。这个算法是稳定的。
事实上unique并不会改变[first,last)的元素个数,有一些残余数据会留下来,类比remove操作。
unique有两个版本,因为所谓"相邻元素是否重复"可有不同的定义。第一个版本使用简单的相等equality测试,第二个版本使用一个Binary Predicate binary_pred作为测试准则。以下之列出第一个版本的源码:
template<class ForwardIterator>
ForwardIterator unique(ForwardIterator first, ForwardIterator last) {first = adjacent_find(first, last);return unique_copy(first, last, first);
}
稍后会对unique_copy进行说明。
unique_copy
算法unique_copy可以从[first, last)中将元素复制到以result开头的区间上;如果面对相邻重复元素群,只会复制其中第一个元素。返回迭代器result开头区间的尾端。
与其他名为*_copy的算法一样,unique_copy乃是unique的一个复制式版本,所以它的特性与unique完全相同,只不过将结果输出到另一个区间而已。
unique_copy有两个版本,因为所谓“相邻元素是否重复”可有不同的定义。第一版本使用简单的相等equality测试,第二版本使用一个Binary Predicate binary_pred作为测试准则。以下之类出版本一的源码。
template <class InputIterator, class OutputIterator>
inline OutputIterator unique_copy(InputIterator first, InputIterator last, OutputIterator result) {if (first == last ) return result;return __unique_copy(first, last, result, iterator_category(first));
}template <class InputIterator, class ForwardIterator>
OutputIterator __unique_copy(InputIterator first, InputIterator last, OutputIterator result, forward_iterator_tag) {*result = *first;while(++first != last) if (*result != *first) *++result = *first;return ++result;
}// 辅助函数,output_iterator_tag版
template <class InputIterator, class OutputIterator>
OutputIterator __unique_copy(InputIterator first, InputIterator last, OutputIterator result, output_iterator_tag) {return __copy(first, last, result, value_type(first));
}// 由于output iterato为write only,无法像forwar iterator那般读取
// 所有不能有类似*result != *first这样的判断操作,所以才需要设计这一版本
template <class InputIterator, class OutputIterator, class T>
OutputIterator __unique_copy(InputIterator first, InputIterator last, OutputIterator result, T*) {T value = *first;*result = value;while(++first != last) if (value != * first) {value = *first;*++result = value;}return ++result;}
参考文档《STL源码剖析》---侯捷