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源碼剖析》