通用算法

 

通用算法

     1.      概述

l   判定函数

 
l   流迭代器
通过用流迭代器来替代循环输出。如:
int b[5]={1,2,3,4,5};
for( int i=0; i<5; i++)
     cout<<b[i]<<" ";
//===========================>
cout<<endl;
copy(b, b+5, ostream_iterator< int>(cout," "));
cout<<endl;
当然也可以用其它如copy_if()等等替代copy()进行输出,它里面的三个参数:第一个参数是输出的起始位置,第二个参数是输出的结束点,第三个参数是迭代器,当迭代器每次输出前与第二个参数比较值为相等时,就结束输出。对于迭代器里面的两个参数,第一个是输出到的目标位置,可是以标准输出,也可以是文件等(ofstream outf(“abc.out”);copy(a,a+5,ostream_iterator<int>(cout,” “));),第二个是输出迭代器所指内容后的下一个输出内容。
l   算法复杂性
2.      函数对象
实现时像函数同时保存状态,使用时不考虑参数个数的一种抽象就是函数对象。也称为函数子(functor)。
l   分类:
发生器:不含有参数且有一个任意类型的返回值的函数对象。如随机数发生器 rand()。
一元函数:只有一个参数且返回一个可能不同类型的值 的函数对象。
二元函数:有两个任意类型的(可能是不同类型)参数,且返回一个任意类型(包括 void)的值的函数对象。
一元判定函数:返回 bool型值的一元函数。
二元判定函数:返回 bool型值的二元函数。
严格弱序:一种更广义的理解“相等”概念的二元判定函数。有些标准容器认为如果两个元素中一个不小于另一个(使用 operator<())那么它们就是相等的。
小于可比较:含有小于运算符 operator< 的类。
可赋值:含有对于同类型指定赋值操作符 operator= 的类。
相等可比较:含有对于同类型相等运算符 operator== 的类。
 
函数对象也可分为标准函数对象(也就是下面的自动创建函数对象,它们是在标准库中定义的,比较简单)和由函数适配器自己定义的函数对象(也就是可调整的函数对象)。
l   自动创建函数对象
头文件 <functional>里定义了大量有用的通用函数对象。这些函数对象都是比较简单的。可以用简单的函数对象构造比较复杂的函数对象。也可以用函数对象适配器来获得一个简单的函数对象,并调整它们用来与操作链中的其它函数对象配合。
函数适配器:
    函数适配器就是用来将函数转化为函数对象,或者将多参数(多元)函数转化为少参数(少元)函数。
1)绑定器 (binder)
通过把一个特殊值绑定到一元函数的参数或二元函数对象的某一个参数上将其转化为一元函数对象。
bind1st: 对于二元函数将特殊值绑定到第 1个参数上(左参数)(如:binde1st(less<int>(),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[]. 记住bind1st里比较时是反的就可以了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<int>(),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<class Op, class T>
binder1st<Op> bind1st(const Op& f, const T& val)
{
Typedef typename Op::first_argument_type Arg1_t;
Return binder1st<Op>(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<Arg, Result>
ptr_fun(Result (*fptr)(Arg))
{
  return pointer_to_unary_function<Arg, Result>(fptr);
}
 
template< class Arg, class Result>
class pointer_to_unary_function : public unary_function<Arg, Result>
{
 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<int,bool> 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算法目录
如果一个函数的声明前没有出现<utility>或<numeric>的头文件,那么它就应该出现在<algorithm>中。所有的算法都在名字空间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<int>(),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<InputIterator1, InputIterator2> mismatch(InputIterator1 first1, InputIterator1 last1,  InputIterator2 first2);
pair<InputIterator1, InputIterator2> 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]注意这里要写上类作用域运算符
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值