STL中的常用算法
2013-09-11 17:14
411 查看
一、非变异算法
是一组不破坏操作数据的模板函数,用来对序列数据进行逐个处理、元素查找、子序列搜索、统计和匹配。非变异算法具有极为广泛的适用性,基本上可应用与各种容器。
1查找容器元素find
它用于查找等于某值的元素。它在迭代器区间[first,last)(闭开区间)上查找等于value值的元素,如果迭代器i所指的元素满足*i=value,则返回迭代器i;未找到满足条件的元素,返回last。函数原型:find( v1.begin(), v1.end(), num_to_find );
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
void main()
{
int num_to_find = 6;
vector<int> v1;
for( int i = 0; i < 10; i++ )
v1.push_back(2*i);
vector<int>::iterator result;
result = find( v1.begin(), v1.end(), num_to_find );
if( result == v1.end() )
cout << "未找到任何元素匹配 " << num_to_find << endl;
else
cout << "匹配元素的索引值是 " << result-v1.begin() << endl;
}
2条件查找容器元素find_if
利用返回布尔值的谓词判断pred,检查迭代器区间[first,last)(闭开区间)上的每一个元素,如果迭代器i满足pred(*i)=true,表示找到元素并返回迭代值i(找到的第一个符合条件的元素);未找到元素,返回末位置last。函数原型:find_if(v.begin(),v.end(),divby5);
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
bool divby5(int x)
{
return x%5?0:1;
}
void main()
{
vector<int> v(20);
for(int i=0;i<v.size();i++)
{
v[i]=(i+1)*(i+3);
cout<<v[i]<<' ';
}
cout<<endl;
vector<int>::iterator ilocation;
ilocation=find_if(v.begin(),v.end(),divby5);
if(ilocation!=v.end())
cout<<"找到第一个能被5整除的元素:"<<*ilocation<<endl<<"元素的索引位置是: "<<ilocation-v.begin()<<endl;
}
3统计等于某值的容器元素个数count
list<int> l;
count(l.begin(),l.end(),value)
4条件统计count_if
count_if(l.begin(),l.end(),pred)。谓词pred含义同find_if中的谓词。例子可以参考例2.
5子序列搜索search
search算法函数在一个序列中搜索与另一序列匹配的子序列。参数分别为一个序列的开始位置,结束位置和另一个序列的开始,结束位置。
函数原型:search(v1.begin(),v1.end(),v2.begin(),v2.end());
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
void main()
{
vector<int> v1;
cout<<"v1:";
for(int i=0;i<5;i++)
{
v1.push_back(i+5);
//注意:v1定义时没有给定大小,因此这里不能直接使用赋值语句。
cout<<v1[i]<<' ';
}
cout<<endl;
vector<int> v2;
cout<<"v2:";
for(i=0;i<2;i++)
{
v2.push_back(i+7);
cout<<v2[i]<<' ';
}
cout<<endl;
vector<int>::iterator ilocation;
ilocation=search(v1.begin(),v1.end(),v2.begin(),v2.end());
if(ilocation!=v1.end())
cout<<"v2的元素包含在v1中,起始元素为"<<"v1["<<ilocation-v1.begin()<<']'<<endl;
else
cout<<"v2的元素不包含在v1中"<<endl;
}
6重复元素子序列搜索search_n
search_n算法函数搜索序列中是否有一系列元素值均为某个给定值的子序列。函数原型:search_n(v.begin(),v.end(),3,8),在v中找到3个连续的元素8
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
void main()
{
vector<int> v;
v.push_back(1);
v.push_back(8);
v.push_back(8);
v.push_back(8);
v.push_back(6);
v.push_back(6);
v.push_back(8);
vector<int>::iterator i;
i=search_n(v.begin(),v.end(),3,8);
if(i!=v.end())
cout<<"在v中找到3个连续的元素8"<<endl;
else
cout<<"在v中未找到3个连续的元素8"<<endl;
}
7最后一个子序列搜索find_end
函数原型find_end(v1.begin(),v1.end(),v2.begin(),v2.end());在V1中要求的位置查找V2中要求的序列。
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
void main()
{
vector<int> v1;
v1.push_back(-5);
v1.push_back(1);
v1.push_back(2);
v1.push_back(-6);
v1.push_back(-8);
v1.push_back(1);
v1.push_back(2);
v1.push_back(-11);
vector<int> v2;
v2.push_back(1);
v2.push_back(2);
vector<int>::iterator i;
i=find_end(v1.begin(),v1.end(),v2.begin(),v2.end());
if(i!=v1.end())
cout<<"v1中找到最后一个匹配v2的子序列,位置在" <<"v1["<<i-v1.begin()<<"]"<<endl;
}
二、变异算法
是一组能够修改容器元素数据的模板函数。copy(v.begin(),v.end(),l.begin());将v中的元素复制到l中。
1元素复制copy
#include <vector>
#include <list>
#include <algorithm>
#include <iostream>
using namespace std;
void main()
{
vector<int> v;
v.push_back(1);
v.push_back(3);
v.push_back(5);
list<int> l;
l.push_back(2);
l.push_back(4);
l.push_back(6);
l.push_back(8);
l.push_back(10);
copy(v.begin(),v.end(),l.begin());
list<int>::iterator i;
for(i=l.begin();i!=l.end();i++)
cout<<*i<<' ';
cout<<endl;
}
2元素变换transform改变
函数原型:transform(v.begin(),v.end(),l.begin(),square);也是复制,但是要按某种方案复制。
#include <vector>
#include <list>
#include <algorithm>
#include <iostream>
using namespace std;
int square(int x)
{
return x*x;
}
void main()
{
vector<int> v;
v.push_back(5);
v.push_back(15);
v.push_back(25);
list<int> l(3);
transform(v.begin(),v.end(),l.begin(),square);
list<int>::iterator i;
for(i=l.begin();i!=l.end();i++)
cout<<*i<<' ';
cout<<endl;
}
3替换replace
replace算法将指定元素值替换为新值。
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
void main()
{
vector<int> v;
v.push_back(13);
v.push_back(25);
v.push_back(27);
v.push_back(25);
v.push_back(29);
replace(v.begin(),v.end(),25,100);
vector<int>::iterator i;
for(i=v.begin();i!=v.end();i++)
cout<<*i<<' ';
cout<<endl;
}
输出结果为13 100 27 100 29
4条件替换replace_if
函数原型:replace_if(v.begin(),v.end(),odd,100);
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
bool odd(int x)
{
return x%2;
}
void main()
{
vector<int> v;
for(int i=1;i<10;i++)
v.push_back(i);
replace_if(v.begin(),v.end(),odd,100);
vector<int>::iterator ilocation;
for(ilocation=v.begin();ilocation!=v.end();ilocation++)
cout<<*ilocation<<' ';
cout<<endl;
}
5n次填充fill_n
函数原型fill_n(v.begin(),5,-1);向从v.begin开始的后面5个位置跳入-1
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
void main()
{
vector<int> v(10);
fill_n(v.begin(),5,-1);
vector<int>::iterator ilocation;
for(ilocation=v.begin();ilocation!=v.end();ilocation++)
cout<<*ilocation<<' ';
cout<<endl;
}
输出结果:-1 -1 -1 -1 -1 0 0 0 0 0
6随机生成n个元素generate
函数原型:generate_n(v.begin(),5,rand);向从v.begin开始的后面5个位置随机填写数据。
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
void main()
{
vector<int> v(10);
generate_n(v.begin(),5,rand);
vector<int>::iterator ilocation;
for(ilocation=v.begin();ilocation!=v.end();ilocation++)
cout<<*ilocation<<' ';
cout<<endl;
}
7条件移除remove_if
返回值相当于移除满足条件的元素后形成的新向量的end()值。
函数原型:remove_if(v.begin(),v.end(),even);
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
bool even(int x)
{
return x%2?0:1;
}
void main()
{
vector<int> v;
for(int i=1;i<=10;i++)
v.push_back(i);
vector<int>::iterator ilocation,result;
cout<<"移除前:";
for(ilocation=v.begin();ilocation!=v.end();ilocation++)
cout<<*ilocation<<' ';
cout<<endl;
result=remove_if(v.begin(),v.end(),even);
cout<<"移除后:";
for(ilocation=v.begin();ilocation!=result;ilocation++)
cout<<*ilocation<<' ';
cout<<endl;
}
8剔除连续重复元素unique
函数原型:unique(v.begin(),v.end());
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
void main()
{
vector<int> v;
v.push_back(2);
v.push_back(6);
v.push_back(6);
v.push_back(6);
v.push_back(9);
v.push_back(6);
v.push_back(3);
vector<int>::iterator ilocation,result;
result=unique(v.begin(),v.end());
for(ilocation=v.begin();ilocation!=result;ilocation++)
cout<<*ilocation<<' ';
cout<<endl;
}
输出结果:2 6 9 6 3
三、排序算法
1、创建堆make_heap
2、元素入堆push_heap(默认插入最后一个元素)
3、元素出堆pop_heap(与push_heap一样,pop_heap必须对堆操作才有意义)
#include <vector>??????
#include <algorithm>
#include <iostream>
using namespace std;
void main()
{
vector<int> v;
v.push_back(5);
v.push_back(6);
v.push_back(4);
v.push_back(8);
v.push_back(2);
v.push_back(3);
v.push_back(7);
v.push_back(1);
v.push_back(9);
make_heap(v.begin(),v.end());
v.push_back(20);
push_heap(v.begin(),v.end());
vector<int>::iterator ilocation;
for(ilocation=v.begin();ilocation!=v.end();ilocation++)
cout<<*ilocation<<' ';
cout<<endl;
pop_heap(v.begin(),v.end());
for(ilocation=v.begin();ilocation!=v.end();ilocation++)
cout<<*ilocation<<' ';
cout<<endl;
}
4堆排序sort_heap
使用:
make_heap(v.begin(),v.end());
sort_heap(v.begin(),v.end());
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
void main()
{
vector<int> v;
v.push_back(3);
v.push_back(9);
v.push_back(6);
v.push_back(3);
v.push_back(17);
v.push_back(20);
v.push_back(12);
vector<int>::iterator ilocation;
for(ilocation=v.begin();ilocation!=v.end();ilocation++)
cout<<*ilocation<<' ';
cout<<endl;
make_heap(v.begin(),v.end());
sort_heap(v.begin(),v.end());
for(ilocation=v.begin();ilocation!=v.end();ilocation++)
cout<<*ilocation<<' ';
cout<<endl;
}
输出结果:
3 9 6 3 17 20 12
3 3 6 9 12 17 20
5排序sort
函数原型:sort(v.begin(),v.end());
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
void main()
{
vector<int> v;
v.push_back(2);
v.push_back(8);
v.push_back(-15);
v.push_back(90);
v.push_back(26);
v.push_back(7);
v.push_back(23);
v.push_back(30);
v.push_back(-27);
v.push_back(39);
v.push_back(55);
vector<int>::iterator ilocation;
for(ilocation=v.begin();ilocation!=v.end();ilocation++)
cout<<*ilocation<<' ';
cout<<endl;
sort(v.begin(),v.end());//比较函数默认
for(ilocation=v.begin();ilocation!=v.end();ilocation++)
cout<<*ilocation<<' ';
cout<<endl;
}
我们先来看看std::for_each最简单和直接的用法,接着关注一下std::for_each更多创造性的用法。这个简单而直接的例子首先定义一个函数对象类型,然后调用std::for_each对一个迭代器组进行遍历操作。
#include<algorithm>
#include<vector>
//新建一个函数对象的类型,它有一个定义好的函数操作符
struct OP
{
OP():s(0){}
void operator()(int& v)
{
v=s++;
}
private:
int s;
};
//初始化一个向量,它有10个整数
std::vector<int> vec(10);
//把向量10个整数分别设置从 0-9
std::for_each(vec.begin(), vec.end(), OP());
看了这个代码大家自然地产生了一个疑问,就是只是把向量里10元素设置一下值,为什么为直接使用一个for循环,而要如此费力地使用for_each,并且还要自己定义一个新的类型,这多难受。不错,就上面的例子使用for循环明显有优势,代码里也要显著减少。所以依常规方式使用for_each很多时候并不一定有更优秀的代码,这也是for_each等STL算法一直都难以被大量应用的一个原因:尽管功能强大,但使用不方便。有许多尝试和努力来解决这一不方便,从而STL算法真正成为方便可用的工具。
下面给出一个使用STL函数对象与适配器一个示例,由于这些设施功能仍然简陋,所以不能轻易实现上例的功能,此处的示例是把10个元素都同时加1:
#include<algorithm>
#include<fuctional>
#include<vector>
std::for_each(vec.begin(), vec.end(),
std::bind1st(std::plus<int>(), 1));//参数绑定和执行加法
#include<vector>
#include<algorithm>
#include<boost/lambda/lambda.hpp>
std::vector<int> vec;
{
using namespace boost::lambda;
int s=0;
std::for_each(vec.begin(), vec.end(), (_1=s, ++s));
// 或者也可以
std::for_each(vec.begin(), vec.end(), _1=var(s)++);
}
看到如此精致代码是否已心动,更多内容可以参数boost::lambda库文档(中文)。
#include<vector>
#include<algorithm>
#include<iostream>
#include<iterator>
int main()
{
std::vector<int> vec(10);
int s=0;
std::for_each(vec.begin(), vec.end(), [&s](int& a){ a=s++; });
std::copy(vec.begin(), vec.end(), std::ostream_iterator<int>(std::cout));
}
区别的话这里不多讲,自己百度咯.
for_each(begin,end,function);
先看一个简单易理解的小例子
[cpp]
view plaincopyprint?
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
void print(int elem)
{
cout << elem << ' ';
}
int main()
{
vector<int> coll;
for(int i = 1; i <= 10; ++i)
coll.push_back(i);
for_each(coll.begin(),coll.end(),print);
cout << endl;
return 0;
}
自己运行一下,没有用for()语句来输出是不是显得很清爽?
再来看一个例子
[cpp]
view plaincopyprint?
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
void show(int number) //将排序后的数字输出
{
cout<<number<<endl;
}
int main()
{
vector<int> getnumber;
int x;
int i=0;
do{
cout<<"please enter a number,when you enter 0,this will end"<<endl;
cin>>x;
getnumber.push_back(x);
}while(x!=0);
int j=getnumber.size();
int t;
for(i=0;i<j;i++)
{
for(int m=j-1;m>i;m--)
{
if(getnumber[m-1]>getnumber[m])
{
t=getnumber[m-1];
getnumber[m-1]=getnumber[m];
getnumber[m]=t;
}
}
}
cout<<"the result:"<<endl;
for_each(getnumber.begin(),getnumber.end(),show);
return 0;
}
for_each第一个参数和第二个参数是指定范围的.至于第三个参数,如果是容器,那么参数就是容器,如果是数组,就写指针,第三个参数的调用函数的名字,就是说对于第一个参数和第二个参数指定范围之中的每一个元素都会带入到第三个参数指定的函数中去。
对STL的容器中,遍历是一个非常经常采用的动作,为此STL也提供一个算法,for_each
遍历一个容器,我们第一个想到的是
for (int i = 0;i < a.size();++i)
的循环来实现。
这样做有几个弊端,比如我现在很多地方用vector作为传引用参数进行传递,为了减少对象构造和析构的代价,我一般采用指针来传递,那么我就要在程序很多地方 写上类似下面的代码
for (int i=0;i < a.size();++i)
delete a[i];
虽然代码很多都一样,可是到处泛滥着这种循环语句,让人阅读程序容易和其它释放指针行为产生混淆
而改用for_each实现
如下面sample
[cpp]
view plaincopyprint?
#include <algorithm>
#include <deque>
using namespace std;
template<class T>
class deletePtr
{
public:
int operator()(T *t)
{
printf("%d\n",*t);
delete t;
}
};
deletePtr <int> deleteIntPtr;
int main()
{
deque<int* > xxx;
xxx.push_back(new int(1));
xxx.push_back(new int(2));
xxx.push_back(new int(3));
xxx.push_back(new int(4));
xxx.push_back(new int(5));
for_each(xxx.begin(),xxx.end(),deleteIntPtr);
}
好处有几个:
1:for_each调用容器内部的遍历函数,比我们的++的方式遍历,效率不会低是肯定的
2:delete指针时候,可以再次确认指针类型
3:用仿函数类,编译器在编译期对函数进行展开,实际上没有函数调用的发生
4:加强代码自说明能力,减少循环,提高代码可读性
STL真的很优雅,我发现MFC的那些容器类,除了个CString外,其它的基本没STL原有的容器类好用,而且可移植性STL也具有很大优势。
[cpp]
view plaincopyprint?
#include <iostream>
#include <algorithm>
#include <list>
using namespace std;
class T1
{
public:
virtual void S1()=0;
virtual void S2()=0;
};
class T2 : public T1
{
public:
virtual void S1() {
printf("T2:S1\n");
}
virtual void S2() {
printf("T2:S2\n");
}
};
class T3_1 : public T2
{
public:
virtual void S1() {
printf("T3_1:S1\n");
}
};
class T3_2 : public T2
{
public:
virtual void S1() {
printf("T3_2:S1\n");
}
virtual void S2() {
printf("T3_2:S2\n");
}
};
int main()
{
typedef list<T1 *> TSTTB;
TSTTB list1;
list1.push_back(new T2());
list1.push_back(new T3_1());
list1.push_back(new T3_2());
printf("T2-T3_1-T3_2\n===============================================\n");
printf("调用&T1::S1\n===============================================\n");
for_each(list1.begin(), list1.end(), mem_fun(&T1::S1));
printf("调用&T1::S2\n===============================================\n");
for_each(list1.begin(), list1.end(), mem_fun(&T1::S2));
return 0;
}
关于transform
例如 std::string sl = "hello"; std::transform(sl.begin(), sl.end(), sl.begin(), toupper); 这样得到 sl 值是 大写的 HELLOtransform 是遍历一个容器里面元素 然后执行一个操作第1和2个参数是数据起始和结束位置(迭代器)参数3是写入目标的起始位置参数4是执行的操作(函数)
怎么对容器中的所有对象都进行同一个操作?我们可能首先想到的是用循环来实现。
比如有如下的一个类:
class ClxECS
{
public:
int DoSomething()
{
// 这里以输出一句话来代替具体的操作
cout << "Output from method DoSomething!" << endl;
return 0;
};
};
现在定义如下一个vector:
vector<ClxECS*> vECS;
for(int i = 0; i < 13; i++)
{
ClxECS *pECS = new ClxECS;
vECS.push_back(pECS);
}
如果要对容器vECS中的所有对象都进行DoSomething()的操作,那么下面的循环可能是首先想到的方案:
for(int i = 0; i < vECS.size(); i++)
vECS.at(i)->DoSomething();
当然,我们也可以用iterator:
for(vector<ClxECS*>::iterator it = vECS.begin(); it != vECS.end(); ++it)
(*it)->DoSomething();
但是,有很多C++的高手和牛人们都会给我们一个忠告,那就是:在处理STL里面的容器的时候,尽量不要自己写循环。
那么,我们就只好用STL算法里面的for_each了。
首先,添加如下一个函数:
int DoSomething(ClxECS *pECS)
{
return pECS->DoSomething();
}
然后就可以用for_each来实现我们想要的功能:
for_each(vECS.begin(), vECS.end(), &DoSomething);
说了半天,似乎跟mem_fun和mem_fun_ref没有什么关系。其实,说那么多都是为了引出mem_fun和mem_fun_ref。在用for_each的时候,如果我们不添加上面的那个函数,该怎么办呢?
这个时候就该mem_fun和mem_fun_ref隆重登场了。用如下这一行代码就行了:
for_each(vECS.begin(), vECS.end(), mem_fun(&ClxECS::DoSomething));
不用我多说,大家应该已经明白mem_fun是干什么和该怎么用的了。
mem_fun_ref的作用和用法跟mem_fun一样,唯一的不同就是:当容器中存放的是对象实体的时候用mem_fun_ref,当容器中存放的是对象的指针的时候用mem_fun。
是一组不破坏操作数据的模板函数,用来对序列数据进行逐个处理、元素查找、子序列搜索、统计和匹配。非变异算法具有极为广泛的适用性,基本上可应用与各种容器。
1查找容器元素find
它用于查找等于某值的元素。它在迭代器区间[first,last)(闭开区间)上查找等于value值的元素,如果迭代器i所指的元素满足*i=value,则返回迭代器i;未找到满足条件的元素,返回last。函数原型:find( v1.begin(), v1.end(), num_to_find );
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
void main()
{
int num_to_find = 6;
vector<int> v1;
for( int i = 0; i < 10; i++ )
v1.push_back(2*i);
vector<int>::iterator result;
result = find( v1.begin(), v1.end(), num_to_find );
if( result == v1.end() )
cout << "未找到任何元素匹配 " << num_to_find << endl;
else
cout << "匹配元素的索引值是 " << result-v1.begin() << endl;
}
2条件查找容器元素find_if
利用返回布尔值的谓词判断pred,检查迭代器区间[first,last)(闭开区间)上的每一个元素,如果迭代器i满足pred(*i)=true,表示找到元素并返回迭代值i(找到的第一个符合条件的元素);未找到元素,返回末位置last。函数原型:find_if(v.begin(),v.end(),divby5);
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
bool divby5(int x)
{
return x%5?0:1;
}
void main()
{
vector<int> v(20);
for(int i=0;i<v.size();i++)
{
v[i]=(i+1)*(i+3);
cout<<v[i]<<' ';
}
cout<<endl;
vector<int>::iterator ilocation;
ilocation=find_if(v.begin(),v.end(),divby5);
if(ilocation!=v.end())
cout<<"找到第一个能被5整除的元素:"<<*ilocation<<endl<<"元素的索引位置是: "<<ilocation-v.begin()<<endl;
}
3统计等于某值的容器元素个数count
list<int> l;
count(l.begin(),l.end(),value)
4条件统计count_if
count_if(l.begin(),l.end(),pred)。谓词pred含义同find_if中的谓词。例子可以参考例2.
5子序列搜索search
search算法函数在一个序列中搜索与另一序列匹配的子序列。参数分别为一个序列的开始位置,结束位置和另一个序列的开始,结束位置。
函数原型:search(v1.begin(),v1.end(),v2.begin(),v2.end());
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
void main()
{
vector<int> v1;
cout<<"v1:";
for(int i=0;i<5;i++)
{
v1.push_back(i+5);
//注意:v1定义时没有给定大小,因此这里不能直接使用赋值语句。
cout<<v1[i]<<' ';
}
cout<<endl;
vector<int> v2;
cout<<"v2:";
for(i=0;i<2;i++)
{
v2.push_back(i+7);
cout<<v2[i]<<' ';
}
cout<<endl;
vector<int>::iterator ilocation;
ilocation=search(v1.begin(),v1.end(),v2.begin(),v2.end());
if(ilocation!=v1.end())
cout<<"v2的元素包含在v1中,起始元素为"<<"v1["<<ilocation-v1.begin()<<']'<<endl;
else
cout<<"v2的元素不包含在v1中"<<endl;
}
6重复元素子序列搜索search_n
search_n算法函数搜索序列中是否有一系列元素值均为某个给定值的子序列。函数原型:search_n(v.begin(),v.end(),3,8),在v中找到3个连续的元素8
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
void main()
{
vector<int> v;
v.push_back(1);
v.push_back(8);
v.push_back(8);
v.push_back(8);
v.push_back(6);
v.push_back(6);
v.push_back(8);
vector<int>::iterator i;
i=search_n(v.begin(),v.end(),3,8);
if(i!=v.end())
cout<<"在v中找到3个连续的元素8"<<endl;
else
cout<<"在v中未找到3个连续的元素8"<<endl;
}
7最后一个子序列搜索find_end
函数原型find_end(v1.begin(),v1.end(),v2.begin(),v2.end());在V1中要求的位置查找V2中要求的序列。
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
void main()
{
vector<int> v1;
v1.push_back(-5);
v1.push_back(1);
v1.push_back(2);
v1.push_back(-6);
v1.push_back(-8);
v1.push_back(1);
v1.push_back(2);
v1.push_back(-11);
vector<int> v2;
v2.push_back(1);
v2.push_back(2);
vector<int>::iterator i;
i=find_end(v1.begin(),v1.end(),v2.begin(),v2.end());
if(i!=v1.end())
cout<<"v1中找到最后一个匹配v2的子序列,位置在" <<"v1["<<i-v1.begin()<<"]"<<endl;
}
二、变异算法
是一组能够修改容器元素数据的模板函数。copy(v.begin(),v.end(),l.begin());将v中的元素复制到l中。
1元素复制copy
#include <vector>
#include <list>
#include <algorithm>
#include <iostream>
using namespace std;
void main()
{
vector<int> v;
v.push_back(1);
v.push_back(3);
v.push_back(5);
list<int> l;
l.push_back(2);
l.push_back(4);
l.push_back(6);
l.push_back(8);
l.push_back(10);
copy(v.begin(),v.end(),l.begin());
list<int>::iterator i;
for(i=l.begin();i!=l.end();i++)
cout<<*i<<' ';
cout<<endl;
}
2元素变换transform改变
函数原型:transform(v.begin(),v.end(),l.begin(),square);也是复制,但是要按某种方案复制。
#include <vector>
#include <list>
#include <algorithm>
#include <iostream>
using namespace std;
int square(int x)
{
return x*x;
}
void main()
{
vector<int> v;
v.push_back(5);
v.push_back(15);
v.push_back(25);
list<int> l(3);
transform(v.begin(),v.end(),l.begin(),square);
list<int>::iterator i;
for(i=l.begin();i!=l.end();i++)
cout<<*i<<' ';
cout<<endl;
}
3替换replace
replace算法将指定元素值替换为新值。
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
void main()
{
vector<int> v;
v.push_back(13);
v.push_back(25);
v.push_back(27);
v.push_back(25);
v.push_back(29);
replace(v.begin(),v.end(),25,100);
vector<int>::iterator i;
for(i=v.begin();i!=v.end();i++)
cout<<*i<<' ';
cout<<endl;
}
输出结果为13 100 27 100 29
4条件替换replace_if
函数原型:replace_if(v.begin(),v.end(),odd,100);
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
bool odd(int x)
{
return x%2;
}
void main()
{
vector<int> v;
for(int i=1;i<10;i++)
v.push_back(i);
replace_if(v.begin(),v.end(),odd,100);
vector<int>::iterator ilocation;
for(ilocation=v.begin();ilocation!=v.end();ilocation++)
cout<<*ilocation<<' ';
cout<<endl;
}
5n次填充fill_n
函数原型fill_n(v.begin(),5,-1);向从v.begin开始的后面5个位置跳入-1
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
void main()
{
vector<int> v(10);
fill_n(v.begin(),5,-1);
vector<int>::iterator ilocation;
for(ilocation=v.begin();ilocation!=v.end();ilocation++)
cout<<*ilocation<<' ';
cout<<endl;
}
输出结果:-1 -1 -1 -1 -1 0 0 0 0 0
6随机生成n个元素generate
函数原型:generate_n(v.begin(),5,rand);向从v.begin开始的后面5个位置随机填写数据。
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
void main()
{
vector<int> v(10);
generate_n(v.begin(),5,rand);
vector<int>::iterator ilocation;
for(ilocation=v.begin();ilocation!=v.end();ilocation++)
cout<<*ilocation<<' ';
cout<<endl;
}
7条件移除remove_if
返回值相当于移除满足条件的元素后形成的新向量的end()值。
函数原型:remove_if(v.begin(),v.end(),even);
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
bool even(int x)
{
return x%2?0:1;
}
void main()
{
vector<int> v;
for(int i=1;i<=10;i++)
v.push_back(i);
vector<int>::iterator ilocation,result;
cout<<"移除前:";
for(ilocation=v.begin();ilocation!=v.end();ilocation++)
cout<<*ilocation<<' ';
cout<<endl;
result=remove_if(v.begin(),v.end(),even);
cout<<"移除后:";
for(ilocation=v.begin();ilocation!=result;ilocation++)
cout<<*ilocation<<' ';
cout<<endl;
}
8剔除连续重复元素unique
函数原型:unique(v.begin(),v.end());
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
void main()
{
vector<int> v;
v.push_back(2);
v.push_back(6);
v.push_back(6);
v.push_back(6);
v.push_back(9);
v.push_back(6);
v.push_back(3);
vector<int>::iterator ilocation,result;
result=unique(v.begin(),v.end());
for(ilocation=v.begin();ilocation!=result;ilocation++)
cout<<*ilocation<<' ';
cout<<endl;
}
输出结果:2 6 9 6 3
三、排序算法
1、创建堆make_heap
2、元素入堆push_heap(默认插入最后一个元素)
3、元素出堆pop_heap(与push_heap一样,pop_heap必须对堆操作才有意义)
#include <vector>??????
#include <algorithm>
#include <iostream>
using namespace std;
void main()
{
vector<int> v;
v.push_back(5);
v.push_back(6);
v.push_back(4);
v.push_back(8);
v.push_back(2);
v.push_back(3);
v.push_back(7);
v.push_back(1);
v.push_back(9);
make_heap(v.begin(),v.end());
v.push_back(20);
push_heap(v.begin(),v.end());
vector<int>::iterator ilocation;
for(ilocation=v.begin();ilocation!=v.end();ilocation++)
cout<<*ilocation<<' ';
cout<<endl;
pop_heap(v.begin(),v.end());
for(ilocation=v.begin();ilocation!=v.end();ilocation++)
cout<<*ilocation<<' ';
cout<<endl;
}
4堆排序sort_heap
使用:
make_heap(v.begin(),v.end());
sort_heap(v.begin(),v.end());
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
void main()
{
vector<int> v;
v.push_back(3);
v.push_back(9);
v.push_back(6);
v.push_back(3);
v.push_back(17);
v.push_back(20);
v.push_back(12);
vector<int>::iterator ilocation;
for(ilocation=v.begin();ilocation!=v.end();ilocation++)
cout<<*ilocation<<' ';
cout<<endl;
make_heap(v.begin(),v.end());
sort_heap(v.begin(),v.end());
for(ilocation=v.begin();ilocation!=v.end();ilocation++)
cout<<*ilocation<<' ';
cout<<endl;
}
输出结果:
3 9 6 3 17 20 12
3 3 6 9 12 17 20
5排序sort
函数原型:sort(v.begin(),v.end());
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
void main()
{
vector<int> v;
v.push_back(2);
v.push_back(8);
v.push_back(-15);
v.push_back(90);
v.push_back(26);
v.push_back(7);
v.push_back(23);
v.push_back(30);
v.push_back(-27);
v.push_back(39);
v.push_back(55);
vector<int>::iterator ilocation;
for(ilocation=v.begin();ilocation!=v.end();ilocation++)
cout<<*ilocation<<' ';
cout<<endl;
sort(v.begin(),v.end());//比较函数默认
for(ilocation=v.begin();ilocation!=v.end();ilocation++)
cout<<*ilocation<<' ';
cout<<endl;
}
我们先来看看std::for_each最简单和直接的用法,接着关注一下std::for_each更多创造性的用法。这个简单而直接的例子首先定义一个函数对象类型,然后调用std::for_each对一个迭代器组进行遍历操作。
#include<algorithm>
#include<vector>
//新建一个函数对象的类型,它有一个定义好的函数操作符
struct OP
{
OP():s(0){}
void operator()(int& v)
{
v=s++;
}
private:
int s;
};
//初始化一个向量,它有10个整数
std::vector<int> vec(10);
//把向量10个整数分别设置从 0-9
std::for_each(vec.begin(), vec.end(), OP());
看了这个代码大家自然地产生了一个疑问,就是只是把向量里10元素设置一下值,为什么为直接使用一个for循环,而要如此费力地使用for_each,并且还要自己定义一个新的类型,这多难受。不错,就上面的例子使用for循环明显有优势,代码里也要显著减少。所以依常规方式使用for_each很多时候并不一定有更优秀的代码,这也是for_each等STL算法一直都难以被大量应用的一个原因:尽管功能强大,但使用不方便。有许多尝试和努力来解决这一不方便,从而STL算法真正成为方便可用的工具。
STL预定义的函数对象和函数适配器
使用不方便在STL设计之初就已经被认识,所以STL专门提供了一些简化使用设施,这就是预定义的函数对象和函数适配器。这些函数对象和函数适配器对简化算法调用直到一定的作用,关于它的更多内容请参看这里。下面给出一个使用STL函数对象与适配器一个示例,由于这些设施功能仍然简陋,所以不能轻易实现上例的功能,此处的示例是把10个元素都同时加1:
#include<algorithm>
#include<fuctional>
#include<vector>
std::for_each(vec.begin(), vec.end(),
std::bind1st(std::plus<int>(), 1));//参数绑定和执行加法
boost库对STL扩展:bind和lambda
boost对STL进行了很大扩展,并且这种扩展仍然在进行着。在上面的例子中我们看到一个std::bind1st函数,现在boost库对其进行的扩展,使得boost::bind可以做更多的事,同时语法也更统一。boost::lambda 则走得更远,它实际上是使用库的形式实际著名的lambda表达式,也就是“就地”小函数,它让我们定义一个简单而直观的函数变得异常容易,如此诸如std::for_each的算法需要的函数对象也就是更容易做到了。下面使用boost库来实现最初那个示例,你将看到它是如此得简单:#include<vector>
#include<algorithm>
#include<boost/lambda/lambda.hpp>
std::vector<int> vec;
{
using namespace boost::lambda;
int s=0;
std::for_each(vec.begin(), vec.end(), (_1=s, ++s));
// 或者也可以
std::for_each(vec.begin(), vec.end(), _1=var(s)++);
}
看到如此精致代码是否已心动,更多内容可以参数boost::lambda库文档(中文)。
C++0x(下一代C++)的努力:语言级lambda
lambda表达式实际上是函数式语言的核心,理论上说它现在现有的各种语言都可以构成等价。大家可能知道冯诺依曼机,在历史上lambda与冯诺依曼机是等价的。是现代计算机理论的3个源头之一。它如此重要,所以在下一代C++标准里已经把它加入核心语言,如此我们就不再需要boost::lambda库了。下面示例C++0x中最初示例的样子,它在GCC 4.5.1中编译并运行正确。#include<vector>
#include<algorithm>
#include<iostream>
#include<iterator>
int main()
{
std::vector<int> vec(10);
int s=0;
std::for_each(vec.begin(), vec.end(), [&s](int& a){ a=s++; });
std::copy(vec.begin(), vec.end(), std::ostream_iterator<int>(std::cout));
}
区别的话这里不多讲,自己百度咯.
for_each(begin,end,function);
先看一个简单易理解的小例子
[cpp]
view plaincopyprint?
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
void print(int elem)
{
cout << elem << ' ';
}
int main()
{
vector<int> coll;
for(int i = 1; i <= 10; ++i)
coll.push_back(i);
for_each(coll.begin(),coll.end(),print);
cout << endl;
return 0;
}
#include <iostream> #include <vector> #include <algorithm> using namespace std; void print(int elem) { cout << elem << ' '; } int main() { vector<int> coll; for(int i = 1; i <= 10; ++i) coll.push_back(i); for_each(coll.begin(),coll.end(),print); cout << endl; return 0; }
自己运行一下,没有用for()语句来输出是不是显得很清爽?
再来看一个例子
[cpp]
view plaincopyprint?
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
void show(int number) //将排序后的数字输出
{
cout<<number<<endl;
}
int main()
{
vector<int> getnumber;
int x;
int i=0;
do{
cout<<"please enter a number,when you enter 0,this will end"<<endl;
cin>>x;
getnumber.push_back(x);
}while(x!=0);
int j=getnumber.size();
int t;
for(i=0;i<j;i++)
{
for(int m=j-1;m>i;m--)
{
if(getnumber[m-1]>getnumber[m])
{
t=getnumber[m-1];
getnumber[m-1]=getnumber[m];
getnumber[m]=t;
}
}
}
cout<<"the result:"<<endl;
for_each(getnumber.begin(),getnumber.end(),show);
return 0;
}
#include<iostream> #include<vector> #include<algorithm> using namespace std; void show(int number) //将排序后的数字输出 { cout<<number<<endl; } int main() { vector<int> getnumber; int x; int i=0; do{ cout<<"please enter a number,when you enter 0,this will end"<<endl; cin>>x; getnumber.push_back(x); }while(x!=0); int j=getnumber.size(); int t; for(i=0;i<j;i++) { for(int m=j-1;m>i;m--) { if(getnumber[m-1]>getnumber[m]) { t=getnumber[m-1]; getnumber[m-1]=getnumber[m]; getnumber[m]=t; } } } cout<<"the result:"<<endl; for_each(getnumber.begin(),getnumber.end(),show); return 0; }
for_each第一个参数和第二个参数是指定范围的.至于第三个参数,如果是容器,那么参数就是容器,如果是数组,就写指针,第三个参数的调用函数的名字,就是说对于第一个参数和第二个参数指定范围之中的每一个元素都会带入到第三个参数指定的函数中去。
对STL的容器中,遍历是一个非常经常采用的动作,为此STL也提供一个算法,for_each
遍历一个容器,我们第一个想到的是
for (int i = 0;i < a.size();++i)
的循环来实现。
这样做有几个弊端,比如我现在很多地方用vector作为传引用参数进行传递,为了减少对象构造和析构的代价,我一般采用指针来传递,那么我就要在程序很多地方 写上类似下面的代码
for (int i=0;i < a.size();++i)
delete a[i];
虽然代码很多都一样,可是到处泛滥着这种循环语句,让人阅读程序容易和其它释放指针行为产生混淆
而改用for_each实现
如下面sample
[cpp]
view plaincopyprint?
#include <algorithm>
#include <deque>
using namespace std;
template<class T>
class deletePtr
{
public:
int operator()(T *t)
{
printf("%d\n",*t);
delete t;
}
};
deletePtr <int> deleteIntPtr;
int main()
{
deque<int* > xxx;
xxx.push_back(new int(1));
xxx.push_back(new int(2));
xxx.push_back(new int(3));
xxx.push_back(new int(4));
xxx.push_back(new int(5));
for_each(xxx.begin(),xxx.end(),deleteIntPtr);
}
#include <algorithm> #include <deque> using namespace std; template<class T> class deletePtr { public: int operator()(T *t) { printf("%d\n",*t); delete t; } }; deletePtr <int> deleteIntPtr; int main() { deque<int* > xxx; xxx.push_back(new int(1)); xxx.push_back(new int(2)); xxx.push_back(new int(3)); xxx.push_back(new int(4)); xxx.push_back(new int(5)); for_each(xxx.begin(),xxx.end(),deleteIntPtr); }
好处有几个:
1:for_each调用容器内部的遍历函数,比我们的++的方式遍历,效率不会低是肯定的
2:delete指针时候,可以再次确认指针类型
3:用仿函数类,编译器在编译期对函数进行展开,实际上没有函数调用的发生
4:加强代码自说明能力,减少循环,提高代码可读性
STL真的很优雅,我发现MFC的那些容器类,除了个CString外,其它的基本没STL原有的容器类好用,而且可移植性STL也具有很大优势。
[cpp]
view plaincopyprint?
#include <iostream>
#include <algorithm>
#include <list>
using namespace std;
class T1
{
public:
virtual void S1()=0;
virtual void S2()=0;
};
class T2 : public T1
{
public:
virtual void S1() {
printf("T2:S1\n");
}
virtual void S2() {
printf("T2:S2\n");
}
};
class T3_1 : public T2
{
public:
virtual void S1() {
printf("T3_1:S1\n");
}
};
class T3_2 : public T2
{
public:
virtual void S1() {
printf("T3_2:S1\n");
}
virtual void S2() {
printf("T3_2:S2\n");
}
};
int main()
{
typedef list<T1 *> TSTTB;
TSTTB list1;
list1.push_back(new T2());
list1.push_back(new T3_1());
list1.push_back(new T3_2());
printf("T2-T3_1-T3_2\n===============================================\n");
printf("调用&T1::S1\n===============================================\n");
for_each(list1.begin(), list1.end(), mem_fun(&T1::S1));
printf("调用&T1::S2\n===============================================\n");
for_each(list1.begin(), list1.end(), mem_fun(&T1::S2));
return 0;
}
#include <iostream> #include <algorithm> #include <list> using namespace std; class T1 { public: virtual void S1()=0; virtual void S2()=0; }; class T2 : public T1 { public: virtual void S1() { printf("T2:S1\n"); } virtual void S2() { printf("T2:S2\n"); } }; class T3_1 : public T2 { public: virtual void S1() { printf("T3_1:S1\n"); } }; class T3_2 : public T2 { public: virtual void S1() { printf("T3_2:S1\n"); } virtual void S2() { printf("T3_2:S2\n"); } }; int main() { typedef list<T1 *> TSTTB; TSTTB list1; list1.push_back(new T2()); list1.push_back(new T3_1()); list1.push_back(new T3_2()); printf("T2-T3_1-T3_2\n===============================================\n"); printf("调用&T1::S1\n===============================================\n"); for_each(list1.begin(), list1.end(), mem_fun(&T1::S1)); printf("调用&T1::S2\n===============================================\n"); for_each(list1.begin(), list1.end(), mem_fun(&T1::S2)); return 0; }
关于transform
例如 std::string sl = "hello"; std::transform(sl.begin(), sl.end(), sl.begin(), toupper); 这样得到 sl 值是 大写的 HELLOtransform 是遍历一个容器里面元素 然后执行一个操作第1和2个参数是数据起始和结束位置(迭代器)参数3是写入目标的起始位置参数4是执行的操作(函数)
怎么对容器中的所有对象都进行同一个操作?我们可能首先想到的是用循环来实现。
比如有如下的一个类:
class ClxECS
{
public:
int DoSomething()
{
// 这里以输出一句话来代替具体的操作
cout << "Output from method DoSomething!" << endl;
return 0;
};
};
现在定义如下一个vector:
vector<ClxECS*> vECS;
for(int i = 0; i < 13; i++)
{
ClxECS *pECS = new ClxECS;
vECS.push_back(pECS);
}
如果要对容器vECS中的所有对象都进行DoSomething()的操作,那么下面的循环可能是首先想到的方案:
for(int i = 0; i < vECS.size(); i++)
vECS.at(i)->DoSomething();
当然,我们也可以用iterator:
for(vector<ClxECS*>::iterator it = vECS.begin(); it != vECS.end(); ++it)
(*it)->DoSomething();
但是,有很多C++的高手和牛人们都会给我们一个忠告,那就是:在处理STL里面的容器的时候,尽量不要自己写循环。
那么,我们就只好用STL算法里面的for_each了。
首先,添加如下一个函数:
int DoSomething(ClxECS *pECS)
{
return pECS->DoSomething();
}
然后就可以用for_each来实现我们想要的功能:
for_each(vECS.begin(), vECS.end(), &DoSomething);
说了半天,似乎跟mem_fun和mem_fun_ref没有什么关系。其实,说那么多都是为了引出mem_fun和mem_fun_ref。在用for_each的时候,如果我们不添加上面的那个函数,该怎么办呢?
这个时候就该mem_fun和mem_fun_ref隆重登场了。用如下这一行代码就行了:
for_each(vECS.begin(), vECS.end(), mem_fun(&ClxECS::DoSomething));
不用我多说,大家应该已经明白mem_fun是干什么和该怎么用的了。
mem_fun_ref的作用和用法跟mem_fun一样,唯一的不同就是:当容器中存放的是对象实体的时候用mem_fun_ref,当容器中存放的是对象的指针的时候用mem_fun。
相关文章推荐
- STL常用容器和算法1
- 算法竞赛中常用的C++ STL
- stl常用算法_查找相关
- stl常用算法_算法和生成
- 常用算法 stl综合案例
- 【STL】<algorithm><numeric><functional> 中的常用算法
- STL算法之常用算法
- STL的常用算法
- STL中常用的一些算法函数[持续更新]
- STL常用算法: fill,rotate,rotate_copy.
- STL中比较常用的容器是vector,set和map,比较常用的算法有Sort等。
- STL中的常用算法介绍以及使用
- STL常用拷贝替换交换算法
- STL 常用算法
- 常用STL算法整理
- STL常用算法的实现----小话STL(1)
- 常用STL容器及算法举例
- STL常用算法: distance实现,count_if算法实现,bind2nd的使用..
- STL中的常用算法
- STL源码剖析 [SGI常用算法](stl_algobase.h)