4.STL算法

来源:互联网 发布:linux查询命令路径 编辑:程序博客网 时间:2024/06/16 08:20

STL算法采用覆盖(overwrite)模式而非安插(insert)模式,所以调用者必须保证目标区间拥有足够的元素空间,当然,也可以用安插型迭代器将覆盖模式改写成安插模式。

本节介绍的算法,如果涉及到两个区间的,不同区间所属于的容器类型可以不相同。这一点要特别注意。


非变动性算法有以下:

1.for_each(beg,end,UnaryProc op):对区间[beg,end)的每一个元素调用op(elem)。

值得注意的是,返回的也是一个在算法内部变动过的副本,当使用仿函数的时候,返回的也是一个仿函数。上一小节有一个例子。

这里的一元操作不是调用时传递进来的参数个数,而是重载()需要的参数个数。

op可以变动元素,可以区分for_each()和transform()之间的比较。

op的任何返回值都会被忽略。所以在for_each中调用预定义仿函数会没有效果。

#include <iostream>#include <vector>#include <string>#include <algorithm>#include <iterator>#include <functional>using namespace std;class Addvalue1{private:int theValue;public:explicit Addvalue1(int v):theValue(v){}void operator()(int &elem){elem += theValue;}};class Addvalue2{private:int theValue;public:explicit Addvalue2(int v):theValue(v){}int operator()(int elem){return elem + theValue;}};int main(){vector<int> ivec;ivec.push_back(1);ivec.push_back(2);//输出:1 2copy(ivec.begin(),ivec.end(),ostream_iterator<int>(cout," "));cout << endl;//输出:11 12  for_each(ivec.begin(),ivec.end(),Addvalue1(10));copy(ivec.begin(),ivec.end(),ostream_iterator<int>(cout," "));cout << endl;//输出:11 12  因为for_each是忽略op的返回值的for_each(ivec.begin(),ivec.end(),bind2nd(plus<int>(),10));copy(ivec.begin(),ivec.end(),ostream_iterator<int>(cout," "));cout << endl;//输出:21 22transform(ivec.begin(),ivec.end(),ivec.begin(),Addvalue2(10));copy(ivec.begin(),ivec.end(),ostream_iterator<int>(cout," "));cout << endl;//输出:31 32transform(ivec.begin(),ivec.end(),ivec.begin(),bind2nd(plus<int>(),10));copy(ivec.begin(),ivec.end(),ostream_iterator<int>(cout," "));cout << endl;return 0;}

2.count(beg,end,value):返回元素个数。

3.count_if(beg,end,UnaryPredicate op):返回满足某一准则(条件)的元素个数。

2返回元素值等于value的元素个数,3返回op(elem)为true的个数。

关联容器提供了一个等效的成员函数count(),不过不提供count_if()函数。

#include <iostream>#include <vector>#include <string>#include <algorithm>#include <iterator>#include <functional>using namespace std;int main(){vector<int> ivec;for(int i = 1; i <= 9; ++i)ivec.push_back(i);//输出:4cout << count_if(ivec.begin(),ivec.end(),bind2nd(greater_equal<int>(),6));return 0;}


4.min_element(beg,end):返回最小值元素的迭代器。

5.max_element(beg,end):返回最大值元素的迭代器。

其中可以加入第三个参数,ComFunc op,作为比较准则。无op版本,以<作为元素比较的准则。

op(elem1,elem)如果第一个元素小于第二个元素,应当返回true。

如果存在多个最小值或者最大值,返回第一个最小值或者最大值的迭代器。

#include <algorithm>#include <iterator>#include <functional>#include <cmath>using namespace std;class absLess{public:bool operator()(int a, int b) const{return abs(a) < abs(b);}};int main(){vector<int> ivec;for(int i = -3; i <= 9; ++i)ivec.push_back(i);cout << *min_element(ivec.begin(),ivec.end(),absLess()) << endl;cout << *max_element(ivec.begin(),ivec.end(),absLess()) << endl;;return 0;}

6.find(beg,end,value):搜寻等于某值的第一个元素。

7.find_if(beg,end,UnaryPredicaate op):搜寻满足某个准则的第一个元素。

C++11 提供了一个find_if_not函数。

如果没有找到匹配元素,两种形式都返回end()。一般使用find()或者find_if()第一句话就应该判断返回的迭代器是否为end()。

如果是已排序区间,用lower_bound(),upper_bound(),equal_range(),binary_search()获取更好的性能。

关联容器有一个等效的成员函数find()。


8.search(beg1,end1,beg2,end2):搜寻某个子区间第一次出现的位置(迭代器)。

9.find_end(beg1,end1,beg2,end2):搜寻某个子区间最后一次出现的位置(迭代器)。

两种形式都返回在区间[beg1,end1)和区间[beg2,end2)完全吻合的第一个子区间的第一个元素位置或者最后一个元素的位置。

如果没有找到符合条件的子区间,都返回end()。所以使用的第一句话就应该是判断返回的迭代器是否会end()。

两种形式都可以提供第五个参数BinaryPredicate op,使op(elem,searchelem)为true。

#include <iostream>#include <vector>#include <algorithm>using namespace std;int main(){int a[] = { 1, 2, 3, 4, 3, 2, 1, 2, 3, 4, 3, 2, 1, 2, 3, 4 };int b[] = { 2, 3, 4 };vector<int> ivec(a, a + sizeof(a) / sizeof(*a));vector<int> subivec(b, b + sizeof(b) / sizeof(*b));int num = 0;vector<int>::iterator it = ivec.begin();while ( ( it = search(it, ivec.end(), subivec.begin(), subivec.end()) ) != ivec.end()){num++;it++;}//输出: 3cout << "子序列出现了" << num << "次" << endl;return 0;}

10.find_first_of(beg1,end1,beg2,end2):搜寻等于某些元素第一次出现的位置(迭代器)。

11.find_last_of(beg1,end1,beg2,end2):搜寻等于某些元素最后一次出现的位置(迭代器)。

两种形式都返回在一个元素既在区间[beg1,end1),又在区间[beg2,end2)出现的位置,10是找到第一个这样的元素,11是找到最后一个这样的元素。这个位置是相对于第一个区间而言。

如果没有找到符合条件的子区间,都返回end()。所以使用的第一句话就应该是判断返回的迭代器是否会end()。

两种形式都可以提供第五个参数BinaryPredicate op,使op(elem,searchelem)为true。

#include <iostream>#include <vector>#include <algorithm>using namespace std;int main(){int a[] = {1,2,3,4,3,2,1,2,3,4,3,2,1,2,3,4};int b[] = {2,3,4};vector<int> ivec(a,a + sizeof(a)/sizeof(*a));vector<int> subivec(b,b+ sizeof(b)/sizeof(*b));int num = 0;vector<int>::iterator it = find_first_of(ivec.begin(),ivec.end(),subivec.begin(),subivec.end());while(it != ivec.end()){num++;it = find_first_of(++it,ivec.end(),subivec.begin(),subivec.end());}//输出: 13cout << "共含有子序列的元素" << num <<"个" << endl;return 0;}

12.adjacent_find(beg,end):搜寻两个连续且相等的元素。

返回区间中第一对”连续两个相等元素“之中的第一个元素的位置(迭代器)。

提供第三个参数BinaryPredicate op,使op(elem,nextelem)为true。

如果没有找到符合的元素,返回end()。所以使用的第一句话就应该是判断返回的迭代器是否会end()。


13.bool equal(beg,end,cmpbeg):检验两个区间的元素是否相等。

判断[beg,end)内的元素是否都和"以cmpbeg开头的区间"的元素相等。

调用者必须保证"cmpBeg开头的区间"应该有足够多的元素。

返回bool值。

提供第四个参数BinaryPredicate op,使op(elem,cmpelem)为true。

#include <iostream>#include <vector>#include <list>#include <algorithm>#include <iterator>#include <functional>using namespace std;class cmp{public:bool operator()(char a,char b){return toupper(a) == toupper(b);}};int main(){vector<char> ivec;list<char> lst;for(char c = 'a'; c <= 'z'; ++c)ivec.push_back(c);for(char c = 'A'; c <= 'Z'; ++c)lst.push_back(c);bool flag = equal(ivec.begin(),ivec.end(),lst.begin(),cmp());//输出:两个区间的元素相同if(flag){cout << "两个区间的元素相同" << endl;}else{cout << "两个区间的元素不相同" << endl;}return 0;}
这里附带一句,关于比较不计大小写的字符串,最快的方法就是调用stricmp()函数。

——————

变动性算法有以下:

1.copy(sourceBeg,sourceEnd,destBeg):从第一个元素开始,复制某段区间。

2.copy_backward(sourceBeg,sourceEnd,destEnd):从最后一个元素开始,复制某段区间。

这两个算法都是将源区间[SourceBeg,sourceEnd)中的所有元素复制到以destBeg为起点或者以destEnd为终点的目标区间去。

destBeg要保证有足够多的元素,不然使用插入型迭代器。

返回最后一个被复制元素的下一个位置,也就是第一个未被覆盖的元素的位置。

destBeg或destEnd不可处于[SourceBeg,sourceEnd)区间内。

STL没有copy_if()算法。

下面的程序按照Effective STL 第五条,都应该改为assign版本或者insert版本。这里只是为了举例。

#include <iostream>#include <vector>#include <list>#include <algorithm>#include <iterator>#include <functional>using namespace std;int main(){vector<int> ivec;list<int> lst;for(int i = 1; i <= 3; ++i)ivec.push_back(i);//输出:1 2 3copy(ivec.begin(),ivec.end(),ostream_iterator<int>(cout ," "));cout << endl;//输出:3 2 1 //copy(ivec.rbegin(),ivec.rend(),back_inserter(lst));lst.insert(lst.end(),ivec.rbegin(),ivec.rend());copy(lst.begin(),lst.end(),ostream_iterator<int>(cout ," "));cout << endl;//输出:3 2 1reverse_copy(ivec.begin(),ivec.end(),lst.begin());copy(lst.begin(),lst.end(),ostream_iterator<int>(cout ," "));cout << endl;return 0;}

3.transform(sourceBeg,sourceEnd,destBeg,UnaryFunc op):转化区间内的元素。

4.transform(source1Beg,source1end,source2Beg,destBeg,BinaryFunc op):将两序列的元素加以结合。

3是针对源区间[sourceBeg,sourceEnd)中的每一个元素调用op(elem),并将结果写到以destBeg为起始的目标区间中。

4.是针对源区间[source1Beg,source1End)中的每一个元素以及从source2Beg开始的第二个源区间对应元素调用op(source1Elem,source2Elem),并将结果写到以destBeg为起始的目标区间中。

两个算法都返回第一个未被覆盖的元素的位置。

destBeg要保证有足够多的元素,不然使用插入型迭代器。


5.swap_range(beg1,end1,beg2):交换元素内容

将区间[beg1,end1)内的元素和“从beg2开始的区间”内对应的元素互换。

返回第二个区间最后一个被交换元素的下一个元素的位置(迭代器)。

beg2必须保证有足够多的元素进行交换。

两个区间不能重叠。

注意的是,如果要将相同类型的容器元素全部建好,应用swap()成员函数。而如果只是交换同一个容器中的两个元素,可以使用iter_swap()函数。(注意:iter_swap()函数不要求是同一类型的容器。)

#include <iostream>#include <vector>#include <list>#include <algorithm>#include <iterator>#include <functional>using namespace std;int main(){vector<int> ivec;list<int> lst;for(int i = 1; i <= 4; ++i){ivec.push_back(i);lst.push_back(i+1);}//输出:1 2 3 4copy(ivec.begin(),ivec.end(),ostream_iterator<int>(cout ," "));cout << endl;//输出:2 3 4 5copy(lst.begin(),lst.end(),ostream_iterator<int>(cout ," "));cout << endl;vector<int>::iterator beg1 = ivec.begin();vector<int>::iterator end1 = beg1 + 3;list<int>::iterator beg2 = lst.begin();list<int>::iterator pos;//swap(ivec,lst); 编译不能通过,swap()只允许同一类型的容器iter_swap(beg1,beg2);//输出:2 2 3 4copy(ivec.begin(),ivec.end(),ostream_iterator<int>(cout ," "));cout << endl;//输出:1 3 4 5copy(lst.begin(),lst.end(),ostream_iterator<int>(cout ," "));cout << endl;pos = swap_ranges(beg1,end1,beg2);//输出:1 3 4 4copy(ivec.begin(),ivec.end(),ostream_iterator<int>(cout ," "));cout << endl;//输出:2 2 3 5copy(lst.begin(),lst.end(),ostream_iterator<int>(cout ," "));cout << endl;//输出:5if(pos != lst.end()){cout << *pos << endl;}return 0;}


6.replace(beg,end,oldValue,newValue):将具有某特定值的元素替换成另一个值。

7.replace_if(beg,end,UnaryPredicate op, newValue):将符合某准则的元素替换成另一个值。

6将区间[beg,end)之内每一个与oldValue相等的元素替换成newValue。

7将区间[beg,end)之内每一个令一元判断式op(elem)为true的元素替换成newValue。


——————

移除性算法有以下(注意这里将的移除性算法只是在逻辑上移除元素,并没有实际删除元素):

1.remove(beg,end,value):将等于某特定值的元素全部移除。

2.remove_if(beg,end,UnaryPredicate op):将满足某准则的元素全部移除。

两个算法都返回最后一个未被移除元素的下一位置。

这些算法会把原本置于后面的未移除元素向前移动,覆盖被移除元素,而未被移除的元素在相对次序上保持不变。

如果要删除元素,还得搭配erase()函数。

由于这些算法会造成元素的变动,所以不能用于关联容器,关联容器提供了erase()函数。

list也有一个等效的成员函数remove()。


3.unique(beg,end):移除连续重复的元素。

4.unique(beg,end,BinaryPredicate op):移除连续重复的元素。

注意这两个算法都是移除的连续的,重复的元素。所以源序列必须先排序,才能使用这个算法移除所有的重复元素。

两个算法都是返回最后一个未被移除元素的下一个位置。

op(elem,e)是当前判断元素elem和elem的后一个元素e进行判断。如果e符合移除条件,elem再和e后面的一个元素进行判断。

由于这些算法会造成元素的变动,所以不能用于关联容器。

list也有一个等效的成员函数unique()。

#include <iostream>#include <vector>#include <list>#include <string>#include <algorithm>#include <iterator>#include <functional>using namespace std;class bothSpace{public:bool operator()(char elem1,char elem2){return elem1 == ' ' && elem2 == ' ';}};int main(){string str("hello  world  !!");str.erase(unique(str.begin(),str.end(),bothSpace()),str.end());//输出 hello world !!cout << str << endl;return 0;}

——————

变序性算法有以下:

1.reverse(beg,end):逆转元素次序。返回void。

该算法将区间[beg,end)内的元素全部逆序。

list提供了一个等效的成员函数reverse()。


2.rotate(beg,newbeg,end):旋转序列内的元素,返回void。

该算法将区间[beg,end)内的元素进行旋转,执行后*newbeg成为新的第一元素。

必须保证newBeg是[beg,end)内的一个有效位置。

#include <iostream>#include <algorithm>using namespace std;int main(){char c[] = "abcd1234";//最后-1处理'\0'rotate(c, c + 4, c + sizeof(c) / sizeof(*c) - 1);//输出:1234abcdcout << c << endl;return 0;}

3.next_permutation(beg,end):下一个升序排序。返回bool。

4.prev_permutation(beg,end):下一个降序排序。返回bool。

如果要遍历所有的排序,必须先将序列(按升序或降序)排序,然后以循环方式调用next_permutation或prev_permutation(beg,end),直到算法返回false。

#include <iostream>#include <algorithm>#include <iterator>#include <vector>using namespace std;int main(){int a[] = {3,1,2};vector<int> ivec(a,a + sizeof(a)/sizeof(*a));sort(ivec.begin(),ivec.end());do {copy(ivec.begin(),ivec.end(),ostream_iterator<int>(cout," "));cout << endl;} while (next_permutation(ivec.begin(),ivec.end()));return 0;}

5.stable_partition(beg,end,UnaryPredicate op):将符合条件的元素向前移动。

算法将区间[beg,end)中满足一元判断式op(elem)结果为true的元素向前移动。

算法返回令op()结果为false的第一个元素的位置(迭代器)。

stable_partition会保持元素之间的相对次序。

#include <iostream>#include <algorithm>#include <iterator>#include <vector>#include <functional>using namespace std;int main(){vector<int> ivec;for(int i = 1; i <= 5; ++i)ivec.push_back(i);stable_partition(ivec.begin(),ivec.end(),bind2nd(modulus<int>(),2));//输出:1 3 5 2 4copy(ivec.begin(),ivec.end(),ostream_iterator<int>(cout," "));return 0;}


——————

排序算法这里只介绍两个,partial_sort和nth_element。

partial_sort是局部排序,nth_element是根据第n个元素排序。

#include <iostream>#include <vector>#include <algorithm>#include <iterator>using namespace std;int main(){int a[] = {5,3,1,4,2};vector<int>  ivec(a,a + sizeof(a)/sizeof(*a));//只排序前三个元素partial_sort(ivec.begin(),ivec.begin()+3,ivec.end());//输出:1 2 3 5 4 copy(ivec.begin(),ivec.end(),ostream_iterator<int>(cout," "));cout << endl;return 0;}

——————

已序区间算法的性能一般具有对数复杂度,如下:

1.binary_search(beg,end,value):检查某区间是否包含某个元素。返回bool。

如果需要获得被搜寻元素的位置,应使用lower_bound(),upper_bound(),或者equal_range()函数。


2.include(beg,end,searchBeg,searchEnd):检查某区间内每一个元素是否都涵盖在另一区间中。返回bool。

判断已序区间[beg,end)是否包含另外一个已序区间[searchBeg,searchEnd)。


3.lower_bound(beg,end,value):搜寻第一个“大于等于给定值”的元素。

4.upper_bound(beg,end,value):搜寻第一个"大于给定值”的元素。

5.equal_range(beg,end,value):返回“等于给定值”的所有元素构成的区间。

3和4如果没有找到给定元素,返回end()。

5是返回一个pair<iterator,iterator>类型。

关联容器分别提供相应的成员函数,效率更好。


6.merge(source1Beg,source1End,source2Beg,source2End,destBeg):将两个区间的元素合并。

要保证目标区间够大,否则使用插入型迭代器。

list提供了一个特殊的成员函数merge()。


7.set_union(source1Beg,source1End,source2Beg,source2End,destBeg):求两个区间的并集。

8.set_intersection(source1Beg,source1End,source2Beg,source2End,destBeg):求两个区间的交集。

9.set_difference(source1Beg,source1End,source2Beg,source2End,destBeg):求两个区间的差集。

10.set_symmetric_difference(source1Beg,source1End,source2Beg,source2End,destBeg):找出值在两区间之一出现的所有元素。

#include <iostream>#include <algorithm>#include <iterator>#include <vector>#include <list>#include <set>using namespace std;int main(){int a[] = {1,2,3,4};int b[] = {2,3,4,5};vector<int> ivec(a, a + sizeof(a)/sizeof(*a));list<int> lst(b,b + sizeof(b) / sizeof(*b));set<int> iset;//输出:1 2 3 4 5set_union(ivec.begin(),ivec.end(),lst.begin(),lst.end(),ostream_iterator<int>(cout," "));cout << endl;set_intersection(ivec.begin(),ivec.end(),lst.begin(),lst.end(),inserter(iset,iset.begin()));//输出:2 3 4copy(iset.begin(),iset.end(),ostream_iterator<int>(cout," "));return 0;}

——————

数值算法必须加入<numeric>头文件。如下:

1.accumulate(beg,end,initvalue):组合所有元素(求总和,求总积……)。返回一个值。

默认是initValue = initValue + elem。

提供第四个参数BinaryFunc,它针对每一个元素调用表达式initValue = op(initValue,elem)。

如果序列为空,返回initValue。

#include <iostream>#include <algorithm>#include <vector>#include <functional>#include <numeric>using namespace std;int main(){vector<int> ivec;for(int i = 1; i <= 10; ++i)ivec.push_back(i);//输出:55cout << accumulate(ivec.begin(),ivec.end(),0);cout << endl;//输出:362880cout << accumulate(ivec.begin(),ivec.end(),1,multiplies<int>());cout << endl;return 0;}
0 0
原创粉丝点击