STL算法之其他算法_上

        定义于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));
}

        在序列一[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源码剖析》---侯捷

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/482264.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

系统监控——分布式链路追踪系统

摘要 本文深入探讨了分布式链路追踪系统的必要性与实施细节。随着软件架构的复杂化&#xff0c;传统的日志分析方法已不足以应对问题定位的需求。文章首先解释了链路追踪的基本概念&#xff0c;如Trace和Span&#xff0c;并讨论了其基本原理。接着&#xff0c;文章介绍了SkyWa…

IAR中编译下载未下载问题

第一张图片是正常下载&#xff0c;第二张未正常下载。经过查看download选项发现 启用了 suppress download &#xff08;禁用下载)

【UE5 C++】判断两点连线是否穿过球体

目录 前言 原理 代码 测试 结果 前言 通过数学原理判断空间中任意两点的连线是否穿过球体&#xff0c;再通过射线检测检验算法的正确性。 原理 &#xff08;1&#xff09;设球体球心的坐标为 &#xff0c;半径为r&#xff1b; &#xff08;2&#xff09;设线段中A点的坐…

网络安全之IP伪造

眼下非常多站点的涉及存在一些安全漏洞&#xff0c;黑客easy使用ip伪造、session劫持、xss攻击、session注入等手段危害站点安全。在纪录片《互联网之子》&#xff08;建议搞IT的都要看下&#xff09;中。亚伦斯沃茨&#xff08;真实人物&#xff0c;神一般的存在&#xff09;涉…

《运放秘籍》第二部:仪表放大器专项知识点总结

一、差分放大器与仪表放大器的讨论 1.1. 仪放的前世今生——差分放大器原理&#xff1f; 1.2. 差分放大的原理 1.3. 差分放大器检测电流 1.4. 差分放大器端一&#xff1a;输入阻抗 1.5. 差分放大器端二&#xff1a;共模抑制比 1.6. 为什么关注输入阻抗&#xff1f;共模抑…

AJAX一、axios使用,url组成(协议,域名,资源路径)查询参数和化简,错误处理,请求/响应报文,状态码,接口文档,

一、AJAX是什么 概念 &#xff1a; AJAX是一种与服务器&#xff08;后端&#xff09;通信的技术 二、请求库axios的基本用法 1导包 2使用 // 1. 发请求 axios({ url: 请求地址 }).then(res > { // 2.接收并使用数据 }) <body><p class"province"…

关于ConstarintLayout有关的点

目录 一、概述 二、过程。 1、介绍 主要特点 关键概念 使用示例 总结 2、我遇到的问题 问题&#xff1a; 可能的原因&#xff1a; 结论 一、概述 在学习过程中&#xff0c;发现对ConstarintLayout理解不够到位&#xff0c;下面是发现并解决问题过程。 二、过程。 1…

【UE5 C++课程系列笔记】06——定时器的基本使用

目标 使用定时器每秒调用函数打印一句日志信息&#xff0c;并在调用一定次数后停止定时器。 步骤 1. 新建一个Actor类&#xff0c;这里命名为 2. 先在“TimerActor.cpp”中获取定时器管理器 使用定时器管理器来设置定时器&#xff0c;该定时器在2s后启动&#xff0c;然后每…

用c语言完成俄罗斯方块小游戏

用c语言完成俄罗斯方块小游戏 这估计是你在编程学习过程中的第一个小游戏开发&#xff0c;怎么说呢&#xff0c;在这里只针对刚学程序设计的学生&#xff0c;就是说刚接触C语言没多久&#xff0c;有一点功底的学生看看&#xff0c;简陋的代码&#xff0c;简陋的实现&#xff0…

iQOO Neo10系列携三大蓝科技亮相,性能与续航全面升级

11月29日&#xff0c;iQOO Neo10系列正式登场。作为iQOO Neo系列的最新力作&#xff0c;Neo10系列不仅延续了该系列一贯的“双芯”特色&#xff0c;更在性能、续航、屏幕、影像等多个方面实现了全面升级&#xff0c;为用户带来前所未有的使用体验。此次发布的Neo10系列共有两款…

springboot信息化在线教学平台的设计与实现(代码+数据库+LW)

摘要 随着信息技术在管理上越来越深入而广泛的应用&#xff0c;管理信息系统的实施在技术上已逐步成熟。本文介绍了信息化在线教学平台的开发全过程。通过分析信息化在线教学平台管理的不足&#xff0c;创建了一个计算机管理信息化在线教学平台的方案。文章介绍了信息化在线教…

DataX实战|使用Python 构建简易的DataX数据血缘工具(一)

导读&#xff1a; 在这篇文章中&#xff0c;我介绍了如何使用 Python 构建简易的 DataX 数据血缘工具&#xff0c;以便解决 DataXWeb 在查询表上下游关系时的不足。 选择 Flask 作为框架&#xff0c;利用 DataXWeb 的元数据 job_info 表和 job_json&#xff0c;通过 JSON 解析…

Android 14之HIDL转AIDL通信

Android 14之HIDL转AIDL通信 1、interface接口1.1 接口变更1.2 生成hidl2aidl工具1.3 执行hidl2aidl指令1.4 修改aidl的Android.bp文件1.5 创建路径1.6 拷贝生成的aidl到1和current1.7 更新与冻结版本1.8 编译模块接口 2、服务端代码适配hal代码修改2.1 修改Android.bp的hidl依…

51c视觉~YOLO~合集4

我自己的原文哦~ https://blog.51cto.com/whaosoft/12512597 1、Yolo8 1.1、检测PCB元件 技术世界正在以惊人的速度发展&#xff0c;而这种转变的核心是一个革命性的工具 — 计算机视觉。它最有趣的应用之一是电子印刷电路板 &#xff08;PCB&#xff09; 的检测和分析。本文…

基于树莓派的安保巡逻机器人--项目介绍

目录 一、项目简介 二、项目背景 三、作品研发技术方案 作品主要内容&#xff1a; 方案的科学性 设计的合理性 四、作品创新性及特点 五、作品自我评价 本篇为项目“基于树莓派的安保巡逻机器人”介绍博客 演示视频链接&#xff1a; 基于树莓派的安保巡逻机器人_音游…

多点DMALL启动招股:将在港交所上市,聚焦数字零售服务

近日&#xff0c;多点数智有限公司&#xff08;Dmall Inc.&#xff0c;下称“多点”或“多点DMALL”&#xff09;发布全球发售文件&#xff0c;于11月28日至12月3日招股&#xff0c;预计将于2024年12月6日在港交所主板挂牌上市。 招股书显示&#xff0c;多点DMALL本次全球发售的…

挑战用React封装100个组件【007】

项目地址 https://github.com/hismeyy/react-component-100 组件描述 今天的组件是用来展示聊天列表&#xff0c;或者论坛内容列表的组件。配合挑战006的时候开发的组件&#xff0c;可以显示用户的具体信息。 样式展示 前置依赖 今天&#xff0c;我分享的组件&#xff0c;需…

汉字Unicode编码相互转换API集成指南

汉字Unicode编码相互转换API集成指南 引言 在国际化的背景下&#xff0c;字符编码的统一变得尤为重要。Unicode作为一种通用字符集标准&#xff0c;能够支持全球几乎所有的语言文字&#xff0c;包括复杂的汉字系统。对于开发人员来说&#xff0c;掌握如何在不同的编码格式之间…

[Linux] 进程间通信——匿名管道命名管道

标题&#xff1a;[Linux] 进程间通信——匿名管道&&命名管道 水墨不写bug &#xff08;图片来源于网络&#xff09; 目录 一、进程间通信 二、进程间通信的方案——匿名管道 &#xff08;1&#xff09;匿名管道的原理 &#xff08;2&#xff09;使用匿名管道 三、进…

一体化数据安全平台uDSP 入选【年度创新安全产品 TOP10】榜单

近日&#xff0c;由 FreeBuf 主办的 FCIS 2024 网络安全创新大会在上海隆重举行。大会现场揭晓了第十届 WitAwards 中国网络安全行业年度评选获奖名单&#xff0c;该评选自 2015 年举办以来一直饱受赞誉&#xff0c;备受关注&#xff0c;评选旨在以最专业的角度和最公正的态度&…