1、概述
前面整理了 STL 单纯的数据处理算法,因为太多,分为两个部分,今天就把剩余的部分给整理完毕,这样 STL 的内容就告一段落了。
rotate:将 [first, middle) 内的元素和 [middle, last) 内的元素互换。 middle 所指的元素会成为容器的第一个元素。如果有个数字序列 {1,2,3,4,5,6,7},对元素 3 做旋转操作,会形成{3,4,5,6,7,1,2}。迭代器的移动能力,决定了这个算法的效率,所以有很多个版本。代码如下所示:
template<class ForwardIterator>
inline void rotate( ForwardIteartor first,
ForwardIteartor middle,
ForwardIteartor last)
{
if( first == middle || middle == last )
return;
__rotate( first, middle, last, distance_type(first), iterator_category(first));
}
template<class ForwardIterator, class Distance>
void __rotate( ForwardIterator first,
ForwardIterator middle,
ForwardIterator last,
Distance*,
forward_iterator_tag)
{
for(ForwardIterator i = middle; )
{
iter_swap(first,middle); //前段、后段的元素一一交换
++first; //双双前进1
++i;
//以下判断是前段先结束 还是 后段先结束
if( first == middle ) //前段结束
{
if( i == last ) //如果后段也结束,整个就结束了
return;
middle = i; //否则调整,对新的前、后段再作交换
}
else if( i == last ) //后段先结束
i = middle; //调整,准备对新的前、后段再作交换
}
}
template<class BidirectionalIterator, class Distance>
void __rotate( BidirectionalIterator first,
BidirectionalIterator middle,
BidirectionalIterator last,
Distance*,
bidirectional_iterator_tag)
{
reverse(first,middle);
reverse(middle, last);
reverse(first, last);
}
template<class RandomAccessIterator, class Distance>
void __rotate( RandomAccessIterator first,
RandomAccessIterator middle,
RandomAccessIterator last,
Distance*,
random_access_iterator_tag)
{
//以下迭代器相减的操作,只适用于 random_access_iterator
Distance n = __gcd( last - first, middle - first);
while( n-- )
__rotate_cycle(first, last, first+n, middle - first, value_type(first));
}
//最大公因数,利用辗转相除法
template<class EuclideanRingElement>
EuclideanRingElement __gcd( EuclideanRingElement m, EuclideanRingElement n)
{
while( n != 0 )
{
EuclideanRingElement t = m%n;
m = n;
n = t;
}
return m;
}
template<class RandomAccessIterator, class Distance, class T>
void __rotate_cycle(RandomAccessIterator first,
RandomAccessIterator last,
RandomAccessIterator initial,
Distance shift, T*)
{
T value = *initial;
RandomAccessIterator ptr1 = initial;
RandomAccessIterator ptr2 = ptr1 + shift;
while( ptr2 != initial)
{
*ptr1 = *ptr2;
ptr1 = ptr2;
if( last - ptr2 > shift )
ptr2 += shift;
else
ptr2 = first + (shift - (last - ptr2));
}
*ptr1 = value;
}
对于前两种迭代器的算法,很好理解,但是对于随机访问迭代器的就比较麻烦,这里我就不展开整理了,附上链接一份,大家可以仔细的研究一下 __rotate_cycle 详解。
rotate_copy:行为类似 rotate,但产生出来的新序列会被置于 result 所指出的容器总。返回值 OutputIterator 指向新产生的最后元素的下一位置。
template< class ForwardIterator, class OutputIterator>
OutputIterator rotate_copy( ForwardIterator first,
ForwardIterator middle,
ForwardIterator last,
OutputIterator result)
{
return copy(first,middle, copy(middle, last, result));
}
search:在序列一 [first1, last1) 所涵盖的区间中,查找序列二 [first2, last2) 的首次出现点。如果序列一内不存在与序列二完全匹配的子序列,便返回迭代器 last1。
template<class ForwardIterator1, class ForwardIterator2>
inline ForwardIterator1 serch( ForwardIterator1 first1,
ForwardIterator1 last1,
ForwardIterator2 first1,
ForwardIterator2 last2)
{
return __search(first1, last1, first2, last2, distance_type(first1), distance_type(first2));
}
template<class ForwardIterator1, class ForwardIterator2, class Distance1, class Distance2>
ForwardIterator1 __serch( ForwardIterator1 first1,
ForwardIterator1 last1,
ForwardIterator2 first1,
ForwardIterator2 last2,
Distance1*,
Distance2* )
{
Distance1 d1 = 0;
distance(first1, last1, d1);
Distance2 d2 = 0;
distance(first2, last2, d2);
if( d1 < d2 )
return last1;
ForwardIterator1 current1 = first1;
ForwardIterator1 current2 = first2;
while( current2 != last2)
{
if( *current1 == *current2)
{
++current1;
++current2;
}
else
{
if( d1 == d2 )
return last1;
else
{
++current1;
current2 = first2;
--d1;
}
}
}
return first1;
}
search_n:在序列 [first, last) 所涵盖的区间内,查找 "连续 count 个符合条件之元素” 所形成的子序列,并返回一个迭代器指向该子序列起始处。如果找不到这样的子序列,就返回迭代器 last。
template<class ForwardIterator, class Integer, class T>
ForwardIterator search_n( ForwardIterator first, ForwardIterator last, Integer count, const T& value)
{
if( n <= 0 )
return first;
first = find(first, last, value);
while( first != last)
{
Integer n = count -1;
ForwardIterator i = first;
++i;
while( i != last && n != 0 && *i == value )
{
++i;
--n;
}
if( n == 0)
return first;
else
first = find(i, last, value);
}
return last;
}
unique:移除 [first, last) 相邻元素的重复元素。如果想移除所有的重复元素,必须先将序列排序。
template< class ForwardIterator>
ForwardIterator unique( ForwardIterator first, ForwardIterator last)
{
first = adjacent_find( first, last);
return unique_copy(first, last, first);
}
unique_copy:和unique差不多,只是将元素复制到以 result 开头的区间上。
template<class InputIterator, class OutputIterator>
inline OutputIterator unique_copy( InputIterator first,
InputIterator last,
OutputIterator result)
{
if( first == last )
return result;
return __unique_copy( first, last, result, iterator_category(result));
}
//不同的迭代器,不同的版本
template<class InputIterator, class ForwardIterator>
ForwardIterator __unique_copy( InputIterator first,
InputIterator last,
ForwardIterator result,
forward_iterator_tag)
{
*result = *first;
while( ++first != last )
{
if( *result != *first)
*++result = *first;
}
return ++result;
}
//因为这个比较简单,其它版本就不写了
nth_element:重新排列 [first, last),使迭代器 nth 所指的元素,与“整个 [first, last) 完整排序后,同一位置的元素” 同值。
此外并保证 [nth, last) 内没有任何一个元素小于 [first, nth) 内的元素,但对于 [first, nth) 和 [nth, last) 两个子区间内的元素次序则无任何保证。 nth_element 比较类似 partition。
由于 nth_element 比 partial_sort(局部排序) 保证的更少,所以绝大数情况下比 partial_sort 更快。
nth_element 的原理是,不断的以 median-of-3 partitioning(三点中值为枢轴之分割法)将整个序列分割为更小的左、右子序列。如果 nth 迭代器落于左子序列,就再对左子序列进行分割,否则就再对右子序列进行分割。以此类推,直到分割后的子序列长度不大于3,便对最后这个待分割的子序列做 Insertion Sort,大功告成。
template<class RandomAccessIterator>
inline void nth_element( RandomAccessIterator first, RandomAccessIterator nth, RandomAccessIterator last)
{
__nth_element(first, nth, last, value_type(first));
}
template<class RandomAccessIterator, class T>
void __nth_element( RandomAccessIterator first, RandomAccessIterator nth, RandomAccessIterator last, T*)
{
//长度超过3,不断的调用
while( last - first > 3)
{
//采用三点中值分割法,返回一个迭代器,指向分割后的右段第一个元素
RandomAccessIterator cut = __unguarded_partition(first, last,
T(__median(*first, *(first + (last - first)/2), *(last-1))));
if( cut <= nth)
first = cut;
else
last = cut;
}
__insertion_sort(first, last);
}
上面用到的__unguarded_partition 在整理 排序中的 sort 时整理过了,就不重复整理了。
2、排列组合算法
STL 提供了两个用来计算排列组合关系的算法,分别是 next_permucation 和 prev_permutation。
对于三个字符所组成的序列 {a,b,c}。这个序列有六个可能的排列组合: abc,acb,bac,bca,cab,cba。这些排列组合根据 less-than 操作符做字典顺序的排序。abc 名列第一,因为每一个元素都小于其后的元素。acb是次一个排列组合,它是固定了a之后所做的新组合。
2.1、next_permucation
next_permucation() 会取得 [first, last) 所标示之序列的下一个排列组合。如果没有下一个排序组合,便返回 false;否则返回 true。
首先,从最尾端开始往前寻找两个相邻元素,令第一元素为 *i,第二元素为 *ii,且满足 *i < *ii。找到这样一组相邻元素后,再从最尾端开始往前检查,找出第一个大于 *i 的元素,令 i 和 j 的元素对调,再将 ii 之后的所有元素颠倒排序。此即所求之 “下一个” 排列组合。
template<class BidirectionalIterator>
bool next_permutation(BidirectionalIterator first, BidirectionalIterator last)
{
if( first == last )
return false;
BidirectionalIterator i = first;
++i;
if( i == last ) //只有一个元素
return false;
i = last; //指向尾端
--i;
for(;;)
{
BidirectionalIterator ii = i;
--i;
//锁定一组相邻元素
if( *i < *ii )
{
//从尾端往前找,知道遇上比 *i 大的元素
BidirectionalIterator j = last;
while( !(*i < *--j));
//交换 i,j 将 ii 之后的元素进行逆向重排
iter_swap(i,j);
reverse(ii, last);
return true;
}
//进行至最前面了
if( i == first )
{
reverse( first, last); //全部逆向重排
return false;
}
}
}
2.2、prev_permutation
prev_permutation 会取得 “前一个” 排列组合。首先,从最尾端开始往前寻找两个相邻元素,令第一元素为 i,第二元素为ii,且满足 *i > *ii。找到这样一组相邻元素后,再从尾端开始往前检验,找出第一个小于 *i 的元素,将 i,j元素对调,再将 ii 之后的所有元素颠倒排序。代码如下:
template<class BidirectionalIterator>
bool prev_permutation(BidirectionalIterator first, BidirectionalIterator last)
{
if( first == last )
return false;
BidirectionalIterator i = first;
++i;
if( i == last ) //只有一个元素
return false;
i = last; //指向尾端
--i;
for(;;)
{
BidirectionalIterator ii = i;
--i;
//锁定一组相邻元素
if( *i > *ii )
{
//从尾端往前找,知道遇上比 *i 大的元素
BidirectionalIterator j = last;
while( !(*i > *--j));
//交换 i,j 将 ii 之后的元素进行逆向重排
iter_swap(i,j);
reverse(ii, last);
return true;
}
//进行至最前面了
if( i == first )
{
reverse( first, last); //全部逆向重排
return false;
}
}
}
2.3、random_shuffle
random_shuffle 是将 [first, last) 元素次序随机重排。如果 N = last - first,在 N! 种可能的元素排列顺序中随机选出一种。random_shuffle 有两个版本,差别在于随机数的取得。版本一使用内部随机数产生器,版本二使用一个会产生随机数的仿函数。代码如下:
template<class RandomAccessIterator>
inline void random_shuffle(RandomAccessIterator first, RandomAccessIterator last)
{
__random_shuffle(first, last, distance_type(first));
}
template<class RandomAccessIterator, class Distance>
void __random_shuffle(RandomAccessIterator first, RandomAccessIterator last, Distance*)
{
if( first == last)
return;
for( RandomAccessIterator i = first + 1; i != last; ++i)
iter_swap(i, first + Distance(rand() % ((i - first) + 1)));
}
OK,STL 的内容整理完毕,继续努力整理归纳其它的内容。
感谢大家,我是假装很努力的YoungYangD(小羊)。
参考资料:
《STL源码剖析》