第九周 标准模板库STL(二)
1.set和multiset
2.map和multimap
3.容器适配器
4.算法(一)
5.算法(二)
STL中的算法大致可以分为以下七类:
(1)不变序列算法
(2)变值算法
(3)删除算法
(4)变序算法
(5)排序算法
(6)有序区间算法
(7)数值算法
大多重载的算法都是有两个版本的,其中一个是用“==”判断元素是否相等,或用“<”来比较大小;而另一个版本多出来一个类型参数“Pred”,以及函数形参“Pred op”,该版本通过表达式“op(x,y)”的返回值是ture还是false,来判断x是否“等于”y,或者x是否“小于”y。
如下面的有两个版本的min_element:
iterate min_element(iterate first,iterate last);
iterate min_element(iterate first,iterate last, Pred op);
(1)不变序列算法
此类算法不会修改算法所作用的容器或对象,适用于顺序容器和关联容器。它们的时间复杂度都是O(n)的,需要遍历容器。
min:求两个对象中较小的(可自定义比较器)
max:求两个对象中较大的(可自定义比较器)
min_element:求区间中的最小值(可自定义比较器)
max_element:求区间中的最大值(可自定义比较器)
for_each:对区间中的每个元素都做某种操作(后面还有一个for_each可以改变容器内的元素)
count:计算区间中等于某值的元素个数
count_if:计算区间中符合某种条件的元素个数
find:在区间中查找等于某值的元素
find_if:在区间中查找符合某条件的元素,返回第一个满足条件的值的迭代器
find_end:在区间中查找另一个区间最后一次出现的位置(可自定义比较器)
find_first_of:在区间中查找第一个出现在另一个区间中的元素 (可自定义比较器)
adjacent_find:在区间中寻找第一次出现连续两个相等元素的位置(可自定义比较器)
search:在区间中查找另一个区间第一次出现的位置(可自定义比较器)
search_n:在区间中查找第一次出现等于某值的连续n个元素(可自定义比较器)
equal:判断两区间是否相等(可自定义比较器)
mismatch:逐个比较两个区间的元素,返回第一次发生不相等的两个元素的位置(可自定义比较器)
lexicographical_compare:按字典序比较两个区间的大小(可自定义比较器)
find
template<class InIt, class T>
InIt find(InIt first, InIt last, const T& val);
返回区间 [first,last) 中的迭代器 i ,使得 * i == val。如果没有找到,返回last。
find_if
template<class InIt, class Pred>
InIt find_if(InIt first, InIt last, Pred pr);
返回区间 [first,last) 中的迭代器 i, 使得 pr(*i) == true
for_each
template<class InIt, class Fun>
Fun for_each(InIt first, InIt last, Fun f);
对[first,last)中的每个元素 e ,执行 f(e) , 要求 f(e)不能改变e。
count:
template<class InIt, class T>
size_t count(InIt first, InIt last, const T& val);
计算[first,last) 中等于val的元素个数
count_if
template<class InIt, class Pred>
size_t count_if(InIt first, InIt last, Pred pr);
计算[first,last) 中符合pr(e) == true 的元素 e的个数
min_element:
template<class FwdIt>
FwdIt min_element(FwdIt first, FwdIt last);
返回[first,last) 中最小元素的迭代器,以 “< ”作比较器。最小指没有元素比它小,而不是它比别的不同元素都小。因为即便a!= b, a
max_element:
template<class FwdIt>
FwdIt max_element(FwdIt first, FwdIt last);
返回[first,last) 中最大元素***(它不小于任何其他元素,但不见得其他不同元素都小于它***)的迭代器,以 “< ”作比较器。
#include
#include
using namespace std;
class A {
public:
int n;
A(int i):n(i) { }
};
bool operator<( const A & a1, const A & a2) {//重载了小于号<
cout << "< called,a1=" << a1.n << " a2=" << a2.n << endl;
if( a1.n == 3 && a2.n == 7)
return true;
return false;
}
int main() {
A aa[] = { 3,5,7,2,1};
cout << min_element(aa,aa+5)->n << endl;
cout << max_element(aa,aa+5)->n << endl;
return 0;
}
输出:
< called,a1=5 a2=3
< called,a1=7 a2=3
< called,a1=2 a2=3
< called,a1=1 a2=3
3
< called,a1=3 a2=5
< called,a1=3 a2=7
< called,a1=7 a2=2
< called,a1=7 a2=1
7
(2)变值算法
此类算法会修改源区间或目标区间元素的值。值被修改的那个区间,不可以是属于关联容器的,因为修改了之后排序就可能失效。
for_each:对区间中的每个元素都做某种操作
copy:复制一个区间到别处
copy_backward:复制一个区间到别处,但目标区前是从后往前被修改的,这个是copy的拓展, 专门应对内存重叠的情况。如,考虑将区间A拷贝到区间B,同时区间A的地址位于区间B 之前且区间A的尾部和区间B的头部重叠,如果使用copy函数,A的首元素拷贝到B的首元素时,A的尾部的元素会被覆盖,解决办法就是copy_backward,先拷贝尾部元素,从后往前复制,避免内存被覆盖。
transform:将一个区间的元素变形后拷贝到另一个区间,变形就是将元素作为参数得到新的值后再拷贝
swap_ranges:交换两个区间内容
fill:用某个值填充区间
fill_n:用某个值替换区间中的n个元素
generate:用某个操作的结果填充区间
generate_n:用某个操作的结果替换区间中的n个元素
replace:将区间中的某个值替换为另一个值
replace_if:将区间中符合某种条件的值替换成另一个值
replace_copy:将一个区间拷贝到另一个区间,拷贝时某个值要换成新值拷过去
replace_copy_if:将一个区间拷贝到另一个区间,拷贝时符合某条件的值要换成新值拷过去
transform
template<class InIt, class OutIt, class Unop>
OutIt transform(InIt first, InIt last, OutIt x, Unop uop);
对[first,last)中的每个迭代器 I ,执行 uop( * I ) ; 并将结果依次放入从 x 开始的地方。要求 uop( * I ) 不得改变原区间 * I 的值。本模板返回值是个迭代器,即 x + (last-first),x 可以和 first相等,即目标区间和原区间可以重叠,就可以改变原区间中的每一个元素了。
#include
#include
#include
#include
#include
#include
using namespace std;
class CLessThen9 {
public:
bool operator()( int n) { return n < 9; }
};
void outputSquare(int value ) { cout << value * value << " "; }
int calculateCube(int value) { return value * value * value; }
main() {
const int SIZE = 10;
int a1[] = { 1,2,3,4,5,6,7,8,9,10};
int a2[] = { 100,2,8,1,50,3,8,9,10,2 };
vector<int> v(a1,a1+SIZE);
ostream_iterator<int> output(cout," ");//ostream_iterator是STL中的一种类模板
random_shuffle(v.begin(),v.end());
cout << endl << "1) ";
copy( v.begin(),v.end(),output);//注意这个copy的用法
copy( a2,a2+SIZE,v.begin());
cout << endl << "2)";
cout << count(v.begin(),v.end(),8);
cout << endl << "3)";
cout << count_if(v.begin(),v.end(),CLessThen9());//CLessThen9()是个对象
cout << endl << "4)";
cout << * (min_element(v.begin(),v.end()));
cout << endl << "5)";
cout << * (max_element(v.begin(),v.end()));
cout << endl << "6) ";
cout << accumulate(v.begin(),v.end(),0);//求和
cout << endl << "7) ";
for_each(v.begin(),v.end(),outputSquare);
vector<int> cubes(SIZE);
transform(a1,a1+SIZE,cubes.begin(),calculateCube);
cout << endl << "8) ";
copy( cubes.begin(),cubes.end(),output);
}
输出:
1)5 4 1 3 7 8 9 10 6 2 //是随机的
2)2
3)6
4)1
5)100
6)193
7)10000 4 64 1 2500 9 64 81 100 4
8)1 8 27 64 125 216 343 512 729 1000
copy函数的高阶用法
ostream_iterator<int> output(cout," ");
定义了一个ostream_iterator的对象,可以通过cout输出以" "(空格)分隔的一个个整数
那么copy(v.begin(),v.end(),output);可以导致v的内容在cout上输出。STL中的实现细节如下:
template<class Inlt, class Outlt>
Outlt copy(InIt first, Inlt last, Outlt x);
本函数对每个在区间[0, last - first)中的N执行一次*(x+N)= *(first+ N),返回x+ N。
对于copy(v.begin(),v.end(),output); first 和last的类型是vector:.const_ iterator,1 output的类型是ostream_ iterator。
copy的源代码:
template<class_ II, class_ _Ol>
inline_Ol copy( _II_ F,_II_L, _OI_X)
{
for(;_ F!=_L;++ _X,++_F)
*_X=*_F;
return( _X);
}
那么这个copy这么实现和ostream_iterator output(cout," ");一起用,打印结果呢?
关于ostream_ iterator, istream_ iterator的例子
int main() {
istream_ iterator<int> inputlnt(cin);
int n1, n2;
n1 =* inputInt; //读入n1
inputInt ++;
n2 = * inputInt; //读入n2
cout << n1<<","<< n2 << endl;
ostream_ iterator<int> outputInt(cout);
*outputlnt=n1 + n2; cout << endl;
inta[5]={ 1,2,3,4,5};
copy(a,a+5,outputInt); //输出整个数组
return 0;
}
#include
#include
#include
#include
tinclude <iterator>
using namespace std;
int main(){
int. a[4]={ 1,2,3,4 };
My_ostream_iterator<int> oit(cout,");
copy(a,a+4,oit); //输出 1*2*3*4*
ofstream oFile("test.txt", ios:out);
My_ostream iterator<int> oitf(oFile,"*");
copy(a,a+4, oitf); //向test.txt文件中写入 1*2*3*4*
oFile.close();
return 0;
}
My_ostream_iterator怎么写??
上面程序中调用语句"copy( a,a+4,oit)"实例化后得到copy如下:
My_ostream_iterator<int> copy(int*_F, int * _L, My_ostream iterator<int> _X)
{
for(;_F!= _L;++_X,++_F)
*_X=*_F;
return( _X);
}
需要的其他操作,才能让编译器通过:重载“++”,“*”,“=”,函数内部*_X是一个函数,返回值是_X的引用,*_X=*_F;等价于_X。operator=(*_F);
My_ostream_iterator类应该重载 “++”和’*'运算符,“=”也应该被重载
#include
template<class T>
class My_ostream iterator:public iterator<output_iterator_tag, T>{
private:
string sep; /分隔符
ostream & os;
public:
My_ostream_iterator(ostream & o, string s):sep(s), os(O){ }
void operator ++(){}; //++只需要有定义即可,不需要做什么
My_ostream_iterator & operator*() { return * this; }
My_ostream_iterator & operator= ( constT & val)
{os<<val<<sep;retum * this; }
}