1、概述
有序区间,顾名思义就是区间内的元素都是经过排序之后的。对于这种类型的区间,有一系列的算法。今天就对这种区间的算法进行整理。
2、includes
判断序列 S2 是否 “涵盖于” 序列 S1,所谓涵盖,意思是 “S2 的每一个元素都出现于 S1”。代码如下:
template<class InputIterator1, class InputIterator2>
bool includes( InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2)
{
while( first1 != last1)
{
if( *first1 < *first2)
++first1;
else if( *first2 > *first1)
return false;
else
{
++first1;
++first2;
}
}
return first2 == last2;
}
3、merge
将两个经过排序的集合 S1 和 S2,合并起来置于另一段空间中。所得结果也是一个有序序列,返回此序列的last迭代器。merge 是一个稳定操作。代码如下:
template<class InputIterator1, class InputIterator2, class OutputIterator>
OutputIterator merge(InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2, OutputIterator result)
{
while( first1 != last1 && first2 != last2)
{
if( *first1 < *first2)
{
*result = *first1;
++first1;
}
else if( *first2 < *first1)
{
*result = *first2;
++first2;
}
else
{
*result = *first1;
++first1;
++first2;
}
++result;
}
copy(first1, last1, copy(first2, last2, result));
}
4、lower_bound
这是二分查找的一种版本,试图在已排序的 [first, last) 中寻找元素 value。如果 [first, last) 具有与 value 相等的元素,便返回一个迭代器,指向其中第一个元素。如果没有这样的元素存在,便返回 “假设这样的元素存在时应该出现的位置”。也就是说,它会返回一个迭代器,指向第一个 “不小于value” 的元素。如下图所示:
upper_bound 在下面整理了。
lower_bound 的代码如下:
template<class ForwardIterator, class T>
ForwardIterator lower_bound( ForwardIterator first, ForwardIterator last, const T& value)
{
return __lower_bound(first, last, value, distance_type(first), inerator_category(first));
}
template<class ForwardIterator, class T, class Distance>
inline ForwardIterator __lower_bound( ForwardIterator first, ForwardIterator last, const T& value, Distance*, forward_iterator_tag)
{
Distance len = 0;
distance( first, last, len); //求取整个区间的长度 len
Distance half;
ForwardIterator middle;
while( len > 0 )
{
half = len >> 1; //除以2,位操作比较快
middle = first; //这一行和下一行是调整middle 节点
advance(middle, half);
if( *middle < value ) //如果中间位置的元素值 < 目标值
{
first = middle; //令 first 指向 middle 的下一位置
++first;
len = len - half -1; //修正 len,回头测试循环的结束条件
}
else
len = half;
}
return first;
}
//前面版本的重载函数,这叫模板多态
template<class RandomAccessIterator, class T, class Distance>
RandomAccessIterator __lower_bound( RandomAccessIteratorfirst, RandomAccessIterator last, const T& value, Distance*, random_access_iterator_tag)
{
Distance len = last - first;
Distance half;
RandomAccessIterator middle;
while( len > 0)
{
half = len >> 1; //除以2,位操作比较快
middle = first + half;
if( *middle < value ) //如果中间位置的元素值 < 目标值
{
first = middle+1; //令 first 指向 middle 的下一位置
len = len - half -1; //修正 len,回头测试循环的结束条件
}
else
len = half;
}
return first;
}
5、upper_bound
upper_bound 和 lower_bound 差不多。它会返回 “在不破坏顺序的情况下,可插入value的最后一个合适位置”。
由于STL 规范“区间圈定” 时的起头和结尾并不对称,左闭右开,所以upper_bound 与 lower_bound 的返回值意义大有不同。如果你查找某值,而它的确出现在区间之内,则 lower_bound 返回的是一个指向该元素的迭代器。然而 upper_bound 不这么做。因为 upper_bound 返回的是在不破坏排序状态的情况下,value可被插入的 “最后一个” 合适位置。如果 value 存在,那么它返回的迭代器将指向value 的下一个位置,而非指向 value 本身。
template<class ForwardIterator, class T>
inline ForwardIterator upper_bound(ForwardIterator first,ForwardIterator last, const T& value)
{
return __upper_bound(first, last, value, distance_type(first), iterator_category(first));
}
template<class ForwardIterator, class T, class Distance>
inline ForwardIterator __upper_bound( ForwardIterator first, ForwardIterator last, const T& value, Distance*, forward_iterator_tag)
{
Distance len = 0;
distance( first, last, len); //求取整个区间的长度 len
Distance half;
ForwardIterator middle;
while( len > 0 )
{
half = len >> 1; //除以2,位操作比较快
middle = first; //这一行和下一行是调整middle 节点
advance(middle, half);
if( value < *middle) //如果中间位置的元素值 > 目标值
len = half; //修正 len,回头测试循环的结束条件
else
{
first = middle; //令 first 指向 middle 的下一位置
++first;
len = len - half -1; //修正 len,回头测试循环的结束条件
}
}
return first;
}
//前面版本的重载函数,这叫模板多态
template<class RandomAccessIterator, class T, class Distance>
RandomAccessIterator __lower_bound( RandomAccessIteratorfirst, RandomAccessIterator last, const T& value, Distance*, random_access_iterator_tag)
{
Distance len = last - first;
Distance half;
RandomAccessIterator middle;
while( len > 0)
{
half = len >> 1; //除以2,位操作比较快
middle = first + half;
if( value < *middle ) //如果中间位置的元素值 > 目标值
len = half; //修正 len,回头测试循环的结束条件
else
{
first = middle+1; //令 first 指向 middle 的下一位置
len = len - half -1; //修正 len,回头测试循环的结束条件
}
}
return first;
}
6、binary_search
binary_search 是一种二分查找法,试图在以排序的 [first, last) 中寻找元素 value。如果 [first, last) 内有等用于 value 的元素,便返回 true,否则返回 false。
binary_search 利用了 lower_bound 的特性,所以底层调用 lower_bound.
tempalte<class ForwardIterator, class T>
bool binary_search(ForwardIterator first, ForwardIterator last, const T& value)
{
ForwardIterator i = lower_bound(first, last, value);
return i != last && !(value < *i);
}
7、equal_range
equal_range 也是二分查找法的一个版本, 试图在已排序的 [first, last) 中寻找 value。它返回一对迭代器 i 和 j,其中 i 是在不破坏次序的前提下,value 可插入的第一个位置(即 lower_bound),j 则是在不破坏次序的前提下, value 可插入的最后一个位置(即 upper_bound )。因此,[i,j) 内的每个元素都等同于 value,而且 [i,j) 是 [first, last) 之中符合此特性的最大子区间。
equal_range 的代码如下:
template<class ForwardIterator, class T>
inline pair<ForwardIterator, ForwardIterator>
equal_range( ForwarIterator first, ForwarIterator last, const T& value)
{
return __equal_range(first, last, value, distance_type(first), iterator_category(first));
}
//Random_access_iterator版本
tempalte<class RandomAccessIterator, class T, class Distance>
pair<RandomAccessIterator, RandomAccessIterator>
__equal_range(RandomAccessIterator first, RandomAccessIterator last,
const T& value, Distance*, random_access_iterator_tag)
{
Distance len = last - first;
Distance half;
RandomAccessIterator middle, left, right;
while( len > 0 )
{
half = len >> 1;
middle = first + half;
if( *middle < value)
{
first = middle + 1;
len = len - half - 1;
}
else if( value < *middle )
len = half;
else
{
left = lower_bound(first, middle, value);
right = upper_bound(++middle, first + len, value);
return pair<...>(left, right);
}
}
return pair<...>( first, first );
}
//forward_iterator 版本
tempalte<class ForwardIterator, class T, class Distance>
pair<ForwardIterator, ForwardIterator>
__equal_range(ForwardIterator first, ForwardIterator last,
const T& value, Distance*, forward_iterator_tag)
{
Distance len = 0;
distance( first, last, len);
Distance half;
RandomAccessIterator middle, left, right;
while( len > 0 )
{
half = len >> 1;
middle = first;
advance(middle, half);
if( *middle < value)
{
first = middle;
++first;
len = len - half - 1;
}
else if( value < *middle )
len = half;
else
{
left = lower_bound(first, middle, value);
advance(first, len);
right = upper_bound(++middle, first, value);
return pair<...>(left, right);
}
}
return pair<...>( first, first );
}
8、inplace_merge
如果两个连接在一起的序列 [first, middle) 和 [middle, last) 都已排序,那么 inplace_merge 可将它们结合成单一一个序列,并扔保持有序,如果原先两个序列式递增排序,执行结果也会是递增排序,如果原先两个序列式递减排序,执行结果也会是递减排序。
inplace_merge 是一种稳定操作。代码如下:
template<class BidirectionalIterator>
inline void inplace_merge(BidirectionalIterator first, BidirectionalIterator middle, BidirectionalIterator last)
{
//如果任何一个序列为空,就什么都不必做
if( first == middle || middle == last)
return;
__inplace_merge_aux(first, middle, last, value_type(first), distance_type(first));
}
template<class BidirectionalIterator, class T, class Distance>
inline void __inplace_merge_aux(BidirectionalIterator first,
BidirectionalIterator middle,
BidirectionalIterator last,
T* , Distance*)
{
Distance len1 = 0;
distance(first, middle, len1);
Distance len2 = 0;
distance(middle, last, len2);
//注意,会使用额外的内存空间
temporary_buffer<BidirectionalIterator, T> buf(first, last);
if( buf.begin() == 0 ) //内存配置失败
__merge_without_buffer(first, middle, last, len1, len2);
else
__merge_adaptive(first, middle, last, len1, len2, buf.begin(), Distance(buf.size())));
}
这个算法如果有额外的内存辅助,效率会好很多。但是在没有缓冲区或缓冲区不足的情况下,也可以运作。但这里对内存配置失败的就不整理了。
//辅助函数。有缓冲区的情况下
template<class BidirectionalIterator, class Distance, class Pointer>
void __merge_adaptive(BidirectionalIterator first,
BidirectionalIterator middle,
BidirectionalIterator last,
Distance len1,
Distance len2,
Pointer buffer,
Distance buffer_size)
{
//缓冲区足够安置序列一
if( len1 <= len2 && len1 <= buffer_size)
{
Pointer end_buffer = copy(first, middle, buffer);
merge(buffer, end_buffer, middle, last, first);
}
else if( len2 <= buffer_size)
{
//缓冲区空间足够安置序列二
Pointer end_buffer = copy(middle, last, buffer);
__merge_backward(first, middle, buffer, end_buffer, last);
}
else
{
//缓冲区空间不足安置任何一个序列
BidirectionalIterator first_cut = first;
BidirectionalIterator second_cut = middle;
Distance len11 = 0;
Distance len22 = 0;
if( len1 > len2)
{
//序列一比较长
len11 = len1/2;
advance(first_cut, len11);
second_cut = lower_bound(middle, last, *frist_cut);
distance(middle, second_cut, len22);
}
else
{
len22 = len2 / 2;
advance(second_cut, len22);
first_cut = upper_bound(first, middle, *second_cut);
distance(first, first_cut, len11);
}
BidirectionalIterator new_middle = __rotate_adaptive(first_cut, middle, second_cut, len1 - len11, len22, buffer, buffer_size);
//针对左端,递归调用
__merge_adaptive(first, first_cut, new_middle, len11, len22, buffer, buffer_size);
//针对右端,递归调用
__merge_adaptive(new_middle, second_cut, last, len1-len11, len2-len22, buffer, buffer_size);
}
}
上述辅助函数首先判断缓冲区是否足以容纳 inplace_merge 所接受的两个序列中的任何一个。如果空间充裕,逻辑就很简单:把两个序列中的某一个 copy 到缓冲区中,在使用 merge 完成其余工作。
但当缓冲区不足以容纳任何一个序列时,我们以递归分割的方式,让处理长度减半,看看能否容纳于缓冲区中。如果能,就切割较长序列,然后通过算法,从较短序列中,查找比切割序列最后一个元素大的第一个元素,两个区间进行旋转操作。如下图:
这样就变成了两段段较小的 序列进行合并,然后递归调用处理左半部分,递归调用处理右半部分。
如果缓冲区的空间还是不足,则调用rotate函数执行,还是在 __rotate_adaptive 函数中。代码如下:
template<class BidirectionalIterator1, class BidirectionalIterator2, class Distance>
BidirectionalIterator1 __rotate_adaptive(BidirectionalIterator1 first,
BidirectionalIterator1 middle,
BidirectionalIterator1 last,
Distance len1,
Distance len2,
BidirectionalIterator2 buffer,
Distance buffer_size )
{
BidirectionalIterator2 buffer_end;
if( len1 > len2 && len2 <= buffer_size)
{
//缓冲区足够安置序列二(较短)
buffer_end = copy(middle, last, buffer);
copy_backward( first, middle, last);
return copy(buffer, buffer_end, first);
}
else if( len1 <= buffer_size)
{
//缓冲区足够安置序列一
buffer_end = copy(first, middle, buffer);
copy(middle, last, first);
return copy_backward(buffer, buffer_end, last);
}
else
{
//缓冲区仍然不足,改用rotate算法,不需要缓冲区
rotate(first, middle, last);
advance(first, len2);
return first;
}
}
感谢大家,我是假装很努力的YoungYangD(小羊)。
参考资料:
《STL源码剖析》