Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1039657
  • 博文数量: 243
  • 博客积分: 3053
  • 博客等级: 中校
  • 技术积分: 2975
  • 用 户 组: 普通用户
  • 注册时间: 2009-05-02 21:11
文章分类

全部博文(243)

文章存档

2013年(2)

2012年(20)

2011年(5)

2010年(114)

2009年(102)

我的朋友

分类:

2010-11-16 12:01:54

通用算法

     1.      概述

l   判定函数

 
l   流迭代器
通过用流迭代器来替代循环输出。如:
int b[5]={1,2,3,4,5};
for(int i=0; i<5; i++)
    cout<
//===========================>
cout<
copy(b, b+5, ostream_iterator<int>(cout," "));
cout<
当然也可以用其它如copy_if()等等替代copy()进行输出,它里面的三个参数:第一个参数是输出的起始位置,第二个参数是输出的结束点,第三个参数是迭代器,当迭代器每次输出前与第二个参数比较值为相等时,就结束输出。对于迭代器里面的两个参数,第一个是输出到的目标位置,可是以标准输出,也可以是文件等(ofstream outf(“abc.out”);copy(a,a+5,ostream_iterator(cout,” “));),第二个是输出迭代器所指内容后的下一个输出内容。
l   算法复杂性
2.      函数对象
实现时像函数同时保存状态,使用时不考虑参数个数的一种抽象就是函数对象。也称为函数子(functor)。
l   分类:
发生器:不含有参数且有一个任意类型的返回值的函数对象。如随机数发生器rand()。
一元函数:只有一个参数且返回一个可能不同类型的值 的函数对象。
二元函数:有两个任意类型的(可能是不同类型)参数,且返回一个任意类型(包括void)的值的函数对象。
一元判定函数:返回bool型值的一元函数。
二元判定函数:返回bool型值的二元函数。
严格弱序:一种更广义的理解“相等”概念的二元判定函数。有些标准容器认为如果两个元素中一个不小于另一个(使用operator<())那么它们就是相等的。
小于可比较:含有小于运算符operator< 的类。
可赋值:含有对于同类型指定赋值操作符 operator= 的类。
相等可比较:含有对于同类型相等运算符 operator== 的类。
 
函数对象也可分为标准函数对象(也就是下面的自动创建函数对象,它们是在标准库中定义的,比较简单)和由函数适配器自己定义的函数对象(也就是可调整的函数对象)。
l   自动创建函数对象
头文件里定义了大量有用的通用函数对象。这些函数对象都是比较简单的。可以用简单的函数对象构造比较复杂的函数对象。也可以用函数对象适配器来获得一个简单的函数对象,并调整它们用来与操作链中的其它函数对象配合。
函数适配器:
    函数适配器就是用来将函数转化为函数对象,或者将多参数(多元)函数转化为少参数(少元)函数。
1)绑定器(binder)
通过把一个特殊值绑定到一元函数的参数或二元函数对象的某一个参数上将其转化为一元函数对象。
bind1st: 对于二元函数将特殊值绑定到第1个参数上(左参数)(如:binde1st(less(),20),就是大于20的值)
bind2nd: 对于二元函数将特殊值绑定到第2个参数上(右参数)
如:
int a[]={10,20,30,40,50};
std::vector<int> arr(a,a+5);
 
//删除大于的数,可以看binder1st<_Fn2> bind1st(const _Fn2& _Func, const _Ty& _Left)
     //也就是把当作less的左参数,arr.erase(std::remove_if(arr.begin(),arr.end(),
std::bind1st(std::less<int>(),30)),arr.end());//arr={10,20,30}
 
//删除小于的数,可以看binder2nd<_Fn2> bind2nd(const _Fn2& _Func, const _Ty& _Right),也就是把当作less的右参数,arr[]<30
arr.erase(std::remove_if(arr.begin(),arr.end(),
       std::bind2nd(std::less(),30)),arr.end());//arr={30}
2)取反器(not)(也就是对返回值取否定 _negate
函数取反器,对函数返回值取反。
not1:二元函数返回值取反器
not2:一元函数返回值取反器
如:
vector<int> arr1(a,a+5);
//not1是对二元函数对象返回值取反
    //删除arr1中不小于(也就是>=)的数
arr1.erase(remove_if(arr1.begin(),arr1.end(),
not1(bind2nd(less<int>(),30)) ),arr1.end());//arr1={10,20}
vector<int>arr2(a,a+5);
//not2对一元函数返加值取反
    //arr2中的元素按非递减(从大到小)的顺序排序
sort(arr2.begin(),arr2.end(),not2(less<int>())); //arr2={50,40,30,20,10}
l   可调整的函数对象
可调整的函数对象都要有函数的类型,它包含嵌套的类型(参数类型和返回值类型),对于一元函数对象包含的嵌套类型是argument_type 和 result_type,二元函数对象包含的嵌套类型是first_argument_type、second_argument_type,和 result_type。如:
template
binder1st bind1st(const Op& f, const T& val)
{
Typedef typename Op::first_argument_type Arg1_t;
Return binder1st(f, Arg1_t(val));
}
这里的Op就表示由binder1st adapt的函数类型,它包含函数的参数类型和返回值类型。如下所示它们在函数调用运算符operator()中的使用:
Typename Op::result_type
Operator()(const typename Op::second_argument_type& x) const;
 
为这些类提供类型名的函数对象称为可调整的函数对象(adaptable function object)。
l   更多的函数对象例子
l   函数指针适配器
用来把现有的普通函数转换为函数对象(functor)的功能。
算法总是要求有一个类似函数的实体,系统可以提供一个指向普通函数或函数对象的指针。当通过普通函数指针调用函数时,就启用了本地函数调用,当通过函数对象指针调用时,就执行函数对象的operator()成员。函数适配器(如binder)不能直接使用原始函数,所以当使用时需要先把原始函数转换为函数对象。这个转换不需要我们手工去做,标准库已为我们提供了一系列适配器来完成这一工作,如函数指针适配器ptr_fun()。Ptr_fun()函数指针适配器可以把一个有参函数指针转换为一个函数对象,注意它不能转换无参函数指针。
STL中,提供了两个版本的ptr_fun(),它们分别是用于一元函数的pointer_to_unary_function(返回一个一元函数对象)和用于二元函数的pointer_to_binary_function(返回一个二元函数对象)。对于非 一元/二元 的函数是无法转换的。
一元版本的定义如下这:
template<class Arg, class Result>
pointer_to_unary_function
ptr_fun(Result (*fptr)(Arg))
{
 return pointer_to_unary_function(fptr);
}
 
template<class Arg, class Result>
class pointer_to_unary_function : public unary_function
{
 Result (*fptr)(Arg); // Stores the f-ptr
public:
 pointer_to_unary_function(Result (*x)(Arg)) : fptr(x) {}
 Result operator()(Arg x) const
 {
    return fptr(x);
 }
};
Arg是原来普通函数的参数类型,Result是普通函数的返回值类型.
用法:
//unary
int d[] = { 123, 94, 10, 314, 315 };
const int DSZ = sizeof d / sizeof *d;
bool isEven(int x) { return x % 2 == 0; }
vector<bool> vb;
//这里不能直接把函数名isEven传给not1,因为not1需要知道它使用函数对象的实际参数的类型和返回值类型,
//而ptr_fun就可以根据它的模板参数推断出这些类型
transform(d, d + DSZ, back_inserter(vb),not1(ptr_fun(isEven)));
   //或如下两行使用方法:
   //pointer_to_unary_function pf(isEven);  
   //transform(d,d+DSZ,back_inserter(vb), not1(pf));
copy(vb.begin(), vb.end(),ostream_iterator<bool>(cout, " "));
cout << endl;
output: 1 0 0 0 1
 
//binary
double db[] = { 01.23, 91.370, 56.661,023.230, 19.959, 1.0, 3.14159 };
const int SZ = sizeof d / sizeof *d;
vector<double> vd;
transform(db, db + SZ, back_inserter(vd),bind2nd(ptr_fun<double, double, double>(pow), 2.0));
copy(vd.begin(), vd.end(),ostream_iterator<double>(cout, " "));
cout << endl;
output: 1.5129 8348.48 3210.47 539.633 398.362
以上方法将普通函数指封装到类里面,通过使用类对象(这里是函数对象)来调用函数,而不是像之前的C/C++里那样直接使用函数指针调用函数。
把一个成员函数转换为可以适于使用的函数对象:
mem_fun()&mem_fun_ref():
用它们通过把成员函数指针作参数来把成员函数转换成函数对象,调用成员函数。
如:
for_each(vs.begin(), vs.end(), mem_fun(&Shape::draw));
find_if(vs.begin(), vs.end(),mem_fun_ref(&string::[a1] empty));
 
l   编写自己的函数对象适配器
3.      STL算法
如果一个函数的声明前没有出现的头文件,那么它就应该出现在中。所有的算法都在名字空间std 中。
1) 简述迭代器
迭代器的类名描述必须写该迭代器的类型相符。
迭代器的几种形式:
                                  i. InputIterator.
只允许单个从序列读入元素的输入迭代器,只可单向移动,前向使用operator++或opertator*。它可以使用 != 或 == 来检测输入迭代器是否到了序列的结束处。只可以从InputIterator中读元素。
                               ii. OutputIterator
只允许单个向序列写入元素的输出迭代器,只可单向移动,前向使用operator++或opertator*。它不可以使用 != 或 == 来检测迭代器。因为假定接收文件可以接收无限个数的对象,而不需要进行结尾检查。只可以向OutputIterator中输出元素。
                            iii. ForwardIterator
可以同时读和写,但仍是只可以单向移动,前向用operator++或operator*。它可以在相同的范围内比较这些迭代器是否相等。
                               iv. BidirectorIterator
类似于ForwardIterator,只是它增加了operator--,可以双向移动。
                                  v. RandomAccessIterator
支持一个常规指针所做的全部操作:它可以双向移动,可以一次只移一个元素可以移多个元素,可以用operator[]作为下标索引,可以从一个迭代器减去另一个迭代器,可以用operator>或operator<过行迭代器的大小比较等等。
2) 实例创建支持的工具
3) 填充和生成
用特定的某个值来填充(fill()/fill_n())容器某个范围的数据,或为容器的某个特定的范围生成(generate()/generate_n())一组值。
注意:fill()/fill_n()填充时填充的值(第3个参数)是值参数传递,而generate()/generate_n()生成的值填充时引用参数传递
4) 计数
count()用来对指定范围内某一元素的个数进行统计
count_if()用来对指定范围内满足某一条件的元素个数进行统计
5) 操作序列
关于移动序列的算法:
复制:
outputIterator copy(InputIterator first, InputIterator last, OutputIterator destination); //从目的序列的第0个元素开始
bidirectionalIterator2 copy_backward(BidirectionalIterator1 first, BidirectionalIterator1 last,
                                   BidirectionalIterator2 destinationEnd); //从目的序列的最后一个元素开始,以 与copy()相反的顺序 操作
它们都是把一个序列A里的元素copy到另一个序列B里面,因为B的空间已分配,所以只能赋值不能向它的末尾插入元素,因为它们是一个“左混洗”/“右混洗”运算,所以源序列里面不能包含目的序列。
倒置序列的顺序:
void reverse(BidirectionalIterator first, BidirectionalIterator last);//把序列顺序颠倒,原序列顺序改变
outputIterator reverse_copy(BidirectionalIterator first, BidirectionalIterator last, OutputIterator destination);//把原序列的顺序颠倒后copy到目的序列中,原序列顺序不变,以 与copy()相反的顺复制
交换:
forwardIterator2 swap_ranges(ForwardIterator1 first1,ForwardIterator1 last1, ForwardIterator2 first2);
//通过交换对应元素来交换相等大小两个范围的内容,如:
v0:1,2,3,4,5,6,7,8,9
swap_ranges(v0.begin(), v0.begin() + half,v0.begin() + v0.size()/2);//5 6 7 8 1 2 3 4 9
rotate:
void rotate(ForwardIterator first, ForwardIterator middle, ForwardIterator last);//把以middle为界的元素互换位置,如:[first,middle), [middle,last]==========>[middle,last],[first,middle)
OutputIterator rotate_copy(ForwardIterator first,ForwardIterator middle, ForwardIterator last,  OutputIterator destination);)
把一组元素生成不同顺序的所有组合:
     注意:使用这些函数时,要先对序列排序sort()
bool next_permutation(BidirectionalIterator first, BidirectionalIterator last); //生成这个序列的不同排列组合,这些组合的先后顺序是字典顺序, 当它的所有组合都排了一遍时就返回false
bool next_permutation(BidirectionalIterator first, BidirectionalIterator last, StrictWeakOrdering
  binary_pred);
bool prev_permutation(BidirectionalIterator first, BidirectionalIterator last); //同next_permutation,只是生成组合的排列次序 与next_permutation的次序相反
bool prev_permutation(BidirectionalIterator first,  BidirectionalIterator last, StrictWeakOrdering
  binary_pred);
把一个序列里面的元素随机顺序重排:
void random_shuffle(RandomAccessIterator first,  RandomAccessIterator last); //把这个序列里的元素随机重排
void random_shuffle(RandomAccessIterator first,  RandomAccessIterator last RandomNumberGenerator& rand);
把一个序列分割并找出满足条件的分界元素:
   //把序列以指定条件划分,并返回分界点,并把满足这个条件的元素移到序列开始位置,partition划分序列后分界点前后部分的元素顺序随机变化,table_partition划分序列后分界点前后部分的元素顺序不变
BidirectionalIterator partition(BidirectionalIterator first, BidirectionalIterator last, Predicate pred); //不稳定的划分
BidirectionalIterator stable_partition(BidirectionalIterator first,  BidirectionalIterator last,
Predicate pred); //稳定的划分
如:-1, 3, 11, 2, 16, 5, 8, 20, 9, 4按大于2进行分割
vector<int>::iterator iit=partition(vi.begin(), vi.end(), bind2nd(greater(),2));
 //vi: 4 3 11 9 16 5 8 20 2 -1
iit=stable_partition(vii.begin(), vii.end(), bind2nd(greater<int>(),2));
//vii: 3 11 16 5 8 20 9 4 -1 2
6) 查找和替换
所有这些算法都用来在某一个范围内查找一个或多个对象,范围由开始的两个迭代器参数定义
InputIterator find(InputIterator first, InputIterator last,  const EqualityComparable& value); //在范围内从开始位置线性查找value,并返回第一次出现的value的位置,找不到则返回last
inputIterator find_if(InputIterator first, InputIterator  last, Predicate pred);//在范围内从开始位置线性查找满足条件的元素,找到则返回true,找不到则返回last
 
找出两个相等且相邻的元素,返回它的iterator,若找不到则返加last(也即.end())
forwardIterator adjacent_find(forwardIterator first,  forwardIterator last);
找出两个相邻且满足binary_pred的元素,若找出则binary_pred为true,且返回找到的这组元素的首元素iterator,找不到则返回last.
forwardIterator adjacent_find(forwardIterator first,  forwardIterator last, BinaryPredicate binary_pred);
 
在第一个序列中找出第二个序列中也出现了的元素,并返回第一次找到的首元素的iterator
forwardIterator1 find_first_of(forwardIterator1 first1,  forwardIterator1 last1, forwardIterator2 first2,
                forwardIterator2 last2);
在第一个序列中找出第二个序列中也出现了的且满足binary_pred的元素,并返回第一次找到的首元素的iterator
forwardIterator1 find_first_of(forwardIterator1 first1, forwardIterator1 last1,forwardIterator2 first2,
                forwardIterator2 last2, BinaryPredicate binary_pred);
 
找出第二个序列在第一个序列中出现的位置,找到则返回它出现的开始位置,否则返回last1
forwardIterator1 search(forwardIterator1 first1, forwardIterator1 last1, forwardIterator2 first2,
                forwardIterator2 last2);
检测第一个序列中是否有一组元素能满足binary_pred,找到则返回这组元素的开始位置iterator,否则返回last1
forwardIterator1 search( forwardIterator1 first1,  forwardIterator1 last1, forwardIterator2 first2,
                forwardIterator2 last2 BinaryPredicate binary_pred);
find_end()类似于search(),不同的地方是find_end是从后往前找,
forwardIterator1 find_end(forwardIterator1 first1,forwardIterator1 last1, forwardIterator2 first2,
                forwardIterator2 last2);
forwardIterator1 find_end(forwardIterator1 first1,  forwardIterator1 last1, forwardIterator2 first2,
                forwardIterator2 last2, BinaryPredicate binary_pred);
 
在序列中查找连续count个value,如果找到就返回它的iterator,否则返回last
forwardIterator search_n(forwardIterator first, forwardIterator last, Size count, const T& value);
在序列中查找连续count个值,这个值与value能够满足binary_pred,如果找到就返回它的iterator,否则返回last
forwardIterator search_n(forwardIterator first, forwardIterator last, Size count, const T& value, BinaryPredicate binary_pred);
 
返回最小值
forwardIterator min_element(forwardIterator first, forwardIterator last);
forwardIterator min_element(forwardIterator first, forwardIterator last, BinaryPredicate binary_pred);
返回最大值
forwardIterator max_element( forwardIterator first, forwardIterator last);
forwardIterator max_element( forwardIterator first, forwardIterator last, BinaryPredicate binary_pred);
 
new_value替换序更列中的old_value
void replace(ForwardIterator first, ForwardIterator last,  const T& old_value, const T& new_value);
new_value替换序列中满足pred条件的元素值
void replace_if(ForwardIterator first, ForwardIterator  last, Predicate pred, const T& new_value);
 
类同replace,不同的地方是replace_copy不改变第一个序列范围,而是把替代的副本赋给result,更换它的值
OutputIterator replace_copy(InputIterator first,  InputIterator last, OutputIterator result,
const T&  old_value, const T& new_value);
OutputIterator replace_copy_if(InputIterator first, InputIterator last, OutputIterator result, Predicate
  pred, const T& new_value);
 
7) 比较范围
比较两个范围的方法
equal()
比较长度相等的两个序列是否它们的元素和顺序都相同,是则返回true,否则返回false.
bool equal(InputIterator first1, InputIterator last1,  InputIterator first2);//由==来比较是否相同
bool equal(InputIterator first1, InputIterator last1,  InputIterator first2 BinaryPredicate binary_pred);//由binary_pred来决定两元素是否相同
 
mismatch()
比较长度相等的两个序列,找出它们从哪个位置开始不同,如果找到不同,则返回(装有第一个序列中不匹配元素iterator和第二个序列中不匹配元素iterator 的) pair对象;若没找到不同的元素(也就是两个序列完全相同),则返回把第二个序列放在一起的第一个序列的last1.
pair mismatch(InputIterator1 first1, InputIterator1 last1,  InputIterator2 first2);
pair mismatch(InputIterator1 first1, InputIterator1 last1,
                                                  InputIterator2 first2, BinaryPredicate binary_pred);
 
lexicographical_compare
比较两个序列,如果找到不同的一对元素,若第一个序列中的元素小于第二个序列中的元素,则返回true,否则返回false。如果扫描完其中的一个序列仍没有找到不同的元素对,若较短的是第一个序列,那么返回true,否则返回false。不要求两序列等长。
bool lexicographical_compare(InputIterator1 first1, InputIterator1 last1,InputIterator2 first2,
                                   InputIterator2 last2);
//用operator<比较
bool lexicographical_compare(InputIterator1 first1,  InputIterator1 last1, InputIterator2 first2,
                                   InputIterator2 last2, BinaryPredicate binary_pred);
//用binary_pred比较
8) 删除
由于STL的通用性,如果删除并销毁一些不可变大小的容器里的元素和改变输入范围的大小是不安全和不合理的。因些这里对删除操作作了一些限制,把删除了元素的序列重排(把删除的元素放在容器的尾部,未删除的元素排在序列的开头且未被删除的元素的顺序仍然保持,也就是分成:[first,new_last),[new_last,last)),返回新的序列末尾标记new_last(就是不包含删除元素,也即指向第一个被删除的元素)。对于[new_last,last)的迭代器是可以解析的,但是那些元素未被指定,应不再使用。如果想消除可改变大小的容器里面的被删除元素时,可以使用erase()完成,或使用resize()。
9) 对已排序的序列进行排序和运算

 [a1]注意这里要写上类作用域运算符
阅读(1077) | 评论(0) | 转发(0) |
0

上一篇:C++ 谓词对象

下一篇:设计模式 

给主人留下些什么吧!~~