中国大学MOOC程序设计与算法(三):C++ 面向对象程序设计 第九周 标准模板库STL(二) 笔记 之 STL算法(一)

第九周 标准模板库STL(二)
1.set和multiset
2.map和multimap
3.容器适配器
4.算法(一)
5.算法(二)

4.算法(一)

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; }
}

你可能感兴趣的:(中国大学MOOC程序设计与算法(三):C++ 面向对象程序设计 第九周 标准模板库STL(二) 笔记 之 STL算法(一))