正如STL為我們封裝了很多數據結構一樣,STL同樣也為我們提供了很多通用算法,例如排序,查找等。這些算法本身實際上就是一種函數模板,它不依賴與具體的類型,而是通過迭代器和模板來實現的。對於通用算法,這裡有一個重要的概念,那就是:算法絕不執行Container操作,它只會使用迭代器來執行它的邏輯,永遠不會直接的去調用容器本身的函數。如果我們為算法提供的是原始迭代器,那麼算法就絕不會改變底層容器的大小。
STL提供的算法庫很龐大,要記下這麼多的算法是很困難的,所幸它幾乎所有的算法都遵循如下的結構形式:
[cpp]
alg (beg, end, other parms);
alg (beg, end, dest, other parms);
alg (beg, end, beg2, other parms);
alg (beg, end, beg2, end2, other parms);
其中,像beg,end,beg2,end2,dest之類的參數都是指迭代器。
要學習STL的算法,我們還是從最基本的查詢算法開始,它的聲明形式如下:
[cpp]
template <class InputIterator, class T>
InputIterator find ( InputIterator first, InputIterator last, const T& value );
可以看出,find函數屬於第一類通用算法,它的作用是在前兩個參數所規定的序列范圍內查找值等於value的元素,並返回其迭代器。這個版本的find函數要求其元素類型必須支持==或!=操作符。
對於查找算法,STL提供的還有find_first_of(和string::find_first_of的用法類似),find_if等等。
接下來是說下排序算法,STL所提供的排序算法要求的迭代器類型都必須是隨機迭代器(可以參考我的上一篇筆記)。下表列出了STL所提供的排序算法:
函數名
功能描述
sort
對給定區間所有元素進行排序
stable_sort
對給定區間所有元素進行穩定排序
partial_sort
對給定區間所有元素部分排序
partial_sort_copy
對給定區間復制並排序
nth_element
找出給定區間的某個位置對應的元素
is_sorted
判斷一個區間是否已經排好序
partition
使得符合某個條件的元素放在前面
stable_partition
相對穩定的使得符合某個條件的元素放在前面
其中,有stable修飾的函數表示該排序算法是穩定的,即對於相等的元素,不改變它的原始序列。
以sort為例,看一下它的聲明形式:
[cpp]
template <class RandomAccessIterator>
void sort ( RandomAccessIterator first, RandomAccessIterator last );
template <class RandomAccessIterator, class Compare>
void sort ( RandomAccessIterator first, RandomAccessIterator last, Compare comp );
first和last這兩個迭代器限定了排序的范圍,第一個版本要求基本元素類型必須支持<運算符,而在第二個版本中,則顯示的通過第三個參數傳遞比較函數,用於進行排序時的比較。其他排序算法的聲明形式大多和此類似,這裡就不做贅述,具體可參考幫助文檔。
然後,我們來關注一下累加算法。先看它的聲明形式:
[cpp]
template <class InputIterator, class T>
T accumulate ( InputIterator first, InputIterator last, T init );
template <class InputIterator, class T, class BinaryOperation>
T accumulate ( InputIterator first, InputIterator last, T init,
BinaryOperation binary_op );
同樣,根據同樣算法的一般形式,前兩個參數規定了一個序列范圍,這個范圍內的元素都將被累加起來,但三個參數指出了累加的初始值。第一個版本需要元素本身支持加法操作,而第二個版本還有第四個參數,這個參數可以傳遞一個函數對象,它提供兩個基本元素的相加操作。需要注意的是,由於該函數模板的實例化是根據第三個參數而來的,因此在傳遞第三個參數時,我們需要格外小心使它的類型與前兩者的元素類型保持一致。下面的例子就是一個容易出錯的典范:
[cpp]
//v is a vector<double>
accumulate<v.begin(), v.end(), 0);
我們的本意是累加一個double型的容器,結果,在傳遞初始值時傳遞了0而不是0.0,那麼accumulate函數模板就被實例化為一個int型的實例,結果造成了意料外的精度損失。
C語言中有這麼一個函數memset,它可以對一片內存區連續的填充值,在STL裡也提供了一些類似的函數,不過它們更加抽象,其作用對象時容器,而非實際的物理內存區。這些函數大多以fill開頭。需要注意的是,其中一些填充函數的使用並不是很安全,需要程序員自己去保證容器的容量足夠大。例如,下面關於對fill_n函數的使用就是不安全的:
[cpp]
vector<int> vec; // empty vector
// disaster: attempts to write to 10 (nonexistent) elements in vec
fill_n(vec.begin(), 10, 0);
fill_n函數並不會幫你去檢查你說指定的容器是否有足夠大的空間容納這些元素,而只是進行簡單的填充。所以,我們常常使用back_inserter來在容器後面安全的插入元素。
[cpp]
vector<int> vec; // empty vector
// ok: back_inserter creates an insert iterator that adds elements to vec
fill_n (back_inserter(vec), 10, 0); // appends 10 elements to vec
back_inserter函數模板返回的是一個迭代器適配器,類似於container adapter,它將封裝一個容器,並產生一個新的對象,這個對象可以作為迭代器來使用,每次通過這個迭代器進行寫操作都會隱式的調用容器的push_back函數從而在容器的最後插入新值。
插入迭代器適配器主要有三種,它們的寫操作對應調用的容器操作如下:
back_inserter push_back
front_inserter push_front
inserter insert
前面說過,使用普通迭代器的通用算法絕不會改變容器的大小,所以要想對容器進行插入操作,必須使用這些插入迭代器適配器。
下面再簡單列舉一下常用的算法的聲明形式,具體使用請查閱幫助文檔。
剔除容器中重復的元素,該函數要求重復元素必須是連續的:
[cpp]
template <class ForwardIterator>
ForwardIterator unique ( ForwardIterator first, ForwardIterator last );
template <class ForwardIterator, class BinaryPredicate>
ForwardIterator unique ( ForwardIterator first, ForwardIterator last,
BinaryPredicate pred );
對容器內容進行拷貝:
[cpp]
template <class InputIterator, class OutputIterator>
OutputIterator copy ( InputIterator first, InputIterator last, OutputIterator result );
替換容器中指定的元素:
[cpp]
template < class ForwardIterator, class T >
void replace ( ForwardIterator first, ForwardIterator last, const T& old_value, const T& new_value );
拷貝內容到另一容器並替換掉指定的元素(該函數並不會改變原來的容器);
[cpp]
template < class InputIterator, class OutputIterator, class T > www.2cto.com
OutputIterator replace_copy ( InputIterator first, InputIterator last, OutputIterator result, const T& old_value, const T& new_value );
計算容器中滿足條件的元素的個數:
[cpp]
template <class InputIterator, class Predicate>
typename iterator_traits<InputIterator>::difference_type
count_if ( ForwardIterator first, ForwardIterator last, Predicate pred );