C++ STL(第二十一篇:算法-- 應用於有序區間的算法)

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章