从零开始学C++之STL(七):剩下5种算法代码分析与使用示例(remove 、rotate 、sort、lower_bound、accumulate)...

一、移除性算法 (remove)

C++ Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
//TEMPLATEFUNCTIONremove_copy
template< class_InIt,
class_OutIt,
class_Ty> inline
_OutIt_Remove_copy(_InIt_First,_InIt_Last,
_OutIt_Dest, const_Ty&_Val,_Range_checked_iterator_tag)
{
//copyomittingeachmatching_Val
_DEBUG_RANGE(_First,_Last);
_DEBUG_POINTER(_Dest);
for(;_First!=_Last;++_First)
if(!(*_First==_Val))
*_Dest++=*_First;
return(_Dest);
}


template< class_InIt,
class_OutIt,
class_Ty> inline
_OutItunchecked_remove_copy(_InIt_First,_InIt_Last,
_OutIt_Dest, const_Ty&_Val)
{
//copyomittingeachmatching_Val
return_STD_Remove_copy(_CHECKED_BASE(_First),_CHECKED_BASE(_Last),_Dest,_Val,
_STD_Range_checked_iterator_tag());
}


//TEMPLATEFUNCTIONremove
template< class_FwdIt,
class_Ty> inline
_FwdItremove(_FwdIt_First,_FwdIt_Last, const_Ty&_Val)
{
//removeeachmatching_Val
_First=find(_First,_Last,_Val);
if(_First==_Last)
return(_First); //emptysequence,alldone
else
{
//nonemptysequence,worthdoing
_FwdIt_First1=_First;
return(_STDEXTunchecked_remove_copy(++_First1,_Last,_First,_Val));
}
}

如下图所示:


假设现在想要remove 的元素是3,则传入到_Remove_copy 函数的3个参数如上图第一行所示,Val 即3。


接着遍历First ~ Last 区间的元素,将非移除元素拷贝到前面,覆盖前面的元素,最后的指向如图,函数返回的是Dest 位置,如下代


码所示:


for(;_First!=_Last;++_First)


if(!(*_First==_Val))


*_Dest++=*_First;


由上图可看出移除性算法并没有改变元素的个数,如果要真正删除,可以将remove 的返回值传入erase 进行删除,如:


v.erase(remove(v.begin(), v.end(), 3), v.end()); 即会将后面两个元素4 和 5 删除掉。


在这里顺便提一下,erase 会使当前迭代器失效,但可以返回下一个迭代器,故如果需要在遍历中删除,下面的做法才是正确的:


C++ Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include
#include

using namespacestd;

intmain( void)
{
inta[]={ 3, 1, 2, 3, 4};
vector< int>v(a,a+ 5);

//for(vector::iteratorit=v.begin();it!=v.end();++it)
//{
//if(*it==3)
//v.erase(it);ERROR!
//else
//cout<<*it<<'';
//}

for(vector< int>::iteratorit=v.begin();it!=v.end();)
{
if(*it== 3)
it=v.erase(it);
else
{
cout<<*it<< '';
++it;
}
}

cout< return 0;
}

示例代码1:

C++ Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include
#include
#include
#include
using namespacestd;

voidprint_element( intn)
{
cout<'';
}


intmain( void)
{
inta[]={ 1, 3, 2, 3, 4, 5};
vector< int>v(a,a+ 6);

for_each(v.begin(),v.end(),print_element);
cout<
/*remove(v.begin(),v.end(),3);
for_each(v.begin(),v.end(),print_element);
cout<

v.erase(remove(v.begin(),v.end(), 3),v.end());
for_each(v.begin(),v.end(),print_element);
cout<
return 0;
}

二、变序性算法(rotate)

C++ Code
1
2
3
4
5
6
7
template< class_FwdIt> inline
voidrotate(_FwdIt_First,_FwdIt_Mid,_FwdIt_Last)
{
//rotate[_First,_Last)
if(_First!=_Mid&&_Mid!=_Last)
_Rotate(_CHECKED_BASE(_First),_CHECKED_BASE(_Mid),_CHECKED_BASE(_Last),_Iter_cat(_First));
}

rotate 调用了_Rotate,实际上_Rotate 继续调用了某个函数,内部实现代码比较长,而且不容易看懂,这边可以看一下简易的等价


版本实现,来自这里,如下:

C++ Code
1
2
3
4
5
6
7
8
9
10
11
12
template< classForwardIterator>
voidrotate(ForwardIteratorfirst,ForwardIteratormiddle,
ForwardIteratorlast)
{
ForwardIteratornext=middle;
while(first!=next)
{
swap(*first++,*next++);
if(next==last)next=middle;
else if(first==middle)middle=next;
}
}

假设一个容器有 1 2 3 4 5 6 六个元素,现在想把 1 2 放到后面去,可以这样写rotate(v.begin(), v.begin()+2, v.end()); 如下图所示:


即将first 与 next 对应的元素互换且不断向前推进,直到first == next 为止。


三、排序算法 (sort)

C++ Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
template< class_RanIt> inline
voidsort(_RanIt_First,_RanIt_Last)
{
//order[_First,_Last),usingoperator<
_DEBUG_RANGE(_First,_Last);
std::_Sort(_CHECKED_BASE(_First),_CHECKED_BASE(_Last),_Last-_First);
}

template< class_RanIt,
class_Pr> inline
voidsort(_RanIt_First,_RanIt_Last,_Pr_Pred)
{
//order[_First,_Last),using_Pred
_DEBUG_RANGE(_First,_Last);
_DEBUG_POINTER(_Pred);
std::_Sort(_CHECKED_BASE(_First),_CHECKED_BASE(_Last),_Last-_First,_Pred);
}

sort 重载了两个版本,第一个版本只有2个参数,默认按从小到大排序(usingoperator<);第二个版本有三个参数,即可以自定义比较逻辑


(_Pred)。它们都用了标准库的std::_Sort, 跟踪进去发现比较复杂,在_Sort 内会根据一些条件选择不同的排序方式,如标准库的堆排序,合并


序,插入排序等。站在使用的角度看,没必要去深究,但如果是想学习相关的排序,那是很好的资源。


示例代码2:

C++ Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include
#include
#include
#include
using namespacestd;

voidprint_element( intn)
{
cout<'';
}

boolmy_greater( inta, intb)
{
returna>b;
}

intmain( void)
{
inta[]={ 1, 2, 3, 4, 5, 6};
vector< int>v(a,a+ 6);

for_each(v.begin(),v.end(),print_element);
cout<
rotate(v.begin(),v.begin()+ 2,v.end());
for_each(v.begin(),v.end(),print_element);
cout<
sort(v.begin(),v.end());
for_each(v.begin(),v.end(),print_element);
cout<
sort(v.begin(),v.end(),my_greater);
for_each(v.begin(),v.end(),print_element);
cout<
return 0;
}


四、已序区间算法 (lower_bound 、upper_bound)


使用这些算法的前提是区间已经是有序的。

C++ Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
//TEMPLATEFUNCTIONlower_bound
template< class_FwdIt,
class_Ty,
class_Diff> inline
_FwdIt_Lower_bound(_FwdIt_First,_FwdIt_Last, const_Ty&_Val,_Diff*)
{
//findfirstelementnotbefore_Val,usingoperator<
_DEBUG_ORDER_SINGLE(_First,_Last, true);
_Diff_Count= 0;
_Distance(_First,_Last,_Count);

for(; 0<_Count;)
{
//divideandconquer,findhalfthatcontainsanswer
_Diff_Count2=_Count/ 2;
_FwdIt_Mid=_First;
std::advance(_Mid,_Count2);
_DEBUG_ORDER_SINGLE(_Mid,_Last, false);

if(_DEBUG_LT(*_Mid,_Val))
_First=++_Mid,_Count-=_Count2+ 1;
else
_Count=_Count2;
}
return(_First);
}

template< class_FwdIt,
class_Ty> inline
_FwdItlower_bound(_FwdIt_First,_FwdIt_Last, const_Ty&_Val)
{
//findfirstelementnotbefore_Val,usingoperator<
_ASSIGN_FROM_BASE(_First,
_Lower_bound(_CHECKED_BASE(_First),_CHECKED_BASE(_Last),_Val,_Dist_type(_First)));
return_First;
}

lower_bound() 返回第一个“大于等于给定值" 的元素位置,其实还重载了另一个low_bound 版本,如下:


C++ Code
1
2
3
4
5
6
7
//TEMPLATEFUNCTIONlower_boundWITHPRED
template< class_FwdIt,
class_Ty,
class_Diff,
class_Pr> inline
_FwdIt_Lower_bound(_FwdIt_First,_FwdIt_Last,
const_Ty&_Val,_Pr_Pred,_Diff*)

也就是可以自定义比较逻辑,需要注意的是如果使用这个版本,那么区间应该本来就是按comp 方法排序的,如下面这句话:


The elements are compared usingoperator<for the first version, andcompfor the second. The elements in the range shall already

besortedaccording to this same criterion (operator<orcomp), or at leastpartitionedwith respect toval.


由于是已序区间,所以函数内用的是二分查找,而两个版本主要的代码不同在于:


_DEBUG_LT(*_Mid,_Val)


_DEBUG_LT_PRED(_Pred, *_Mid, _Val)


upper_bound 与 lower_bound 类似,不过返回的是第一个”大于给定值“ 的元素位置。


示例代码3:

C++ Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include
#include
#include
#include
using namespacestd;

voidprint_element( intn)
{
cout<'';
}


intmain( void)
{
inta[]={ 1, 10, 10, 14, 15, 16};
vector< int>v(a,a+ 6);

for_each(v.begin(),v.end(),print_element);
cout<
vector< int>::iteratorit;
it=lower_bound(v.begin(),v.end(), 10);
if(it!=v.end())
{
cout< }

it=upper_bound(v.begin(),v.end(), 10);
if(it!=v.end())
{
cout< }

return 0;
}


五、数值算法(accumulate)


C++ Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
//TEMPLATEFUNCTIONaccumulate
template< class_InIt,
class_Ty> inline
_Ty_Accumulate(_InIt_First,_InIt_Last,_Ty_Val)
{
//returnsumof_Valandallin[_First,_Last)
_DEBUG_RANGE(_First,_Last);
for(;_First!=_Last;++_First)
_Val=_Val+*_First;
return(_Val);
}

template< class_InIt,
class_Ty> inline
_Tyaccumulate(_InIt_First,_InIt_Last,_Ty_Val)
{
//returnsumof_Valandallin[_First,_Last)
return_Accumulate(_CHECKED_BASE(_First),_CHECKED_BASE(_Last),_Val);
}

//TEMPLATEFUNCTIONaccumulateWITHBINOP
template< class_InIt,
class_Ty,
class_Fn2> inline
_Ty_Accumulate(_InIt_First,_InIt_Last,_Ty_Val,_Fn2_Func)
{
//returnsumof_Valandallin[_First,_Last),using_Func
_DEBUG_RANGE(_First,_Last);
_DEBUG_POINTER(_Func);
for(;_First!=_Last;++_First)
_Val=_Func(_Val,*_First);
return(_Val);
}

template< class_InIt,
class_Ty,
class_Fn2> inline
_Tyaccumulate(_InIt_First,_InIt_Last,_Ty_Val,_Fn2_Func)
{
//returnsumof_Valandallin[_First,_Last),using_Func
return_Accumulate(_CHECKED_BASE(_First),_CHECKED_BASE(_Last),_Val,_Func);
}


accumulate 重载了两个版本,第一个版本实现的是累加,第二个版本带_Func 参数,可以自定义计算,比如累乘等。代码都比较好理解,就不赘述


了。看下面的示例代码4:

C++ Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include
#include
#include
#include
#include
using namespacestd;

voidprint_element( intn)
{
cout<'';
}

intmult( inta, intb)
{
returna*b;
}

intmain( void)
{
inta[]={ 1, 2, 3, 4, 5};
vector< int>v(a,a+ 5);

for_each(v.begin(),v.end(),print_element);
cout<
//累加
cout<0)<
//累乘
cout<1,mult)<
return 0;
}

参考:

C++ primer 第四版
Effective C++ 3rd
C++编程规范


你可能感兴趣的:(从零开始学C++之STL(七):剩下5种算法代码分析与使用示例(remove 、rotate 、sort、lower_bound、accumulate)...)