1. 程式人生 > >STL之演算法

STL之演算法

STL演算法

STL演算法庫

STL庫中關於演算法的內容大部分包含在<algorithm>、<numeric>和<algorithm>中,其中<algorithm>包含了大多數演算法的函式,而一些關於數值處理的函式在<numeric>中。當我們需要用到函式物件相關內容時,則需要包含<function>檔案。

STL演算法分類

STL庫中的演算法大致可以分為以下幾類:

STL演算法分類
非更易型演算法(nonmodifying algorithm)
更易型演算法(modifying algorithm)
移除型演算法(removing algorithm)
變序型演算法(mutating algorithm)
排序演算法(sorted-range algorithm)
數值演算法(numeric algorithm)

當然,這樣簡單的分類是無法將STL中的演算法完全分類開來的,會有一些演算法會存在多個分類中。

非更易型演算法

非更易型演算法既不改動元素的順序,也不改變元素的值。正如前面提高,迭代器是連線演算法和容器的紐帶,因此,它們可通過迭代器作用於所有的標準容器上

下面列出STL中的非更易型演算法:

函式 作用
count() 返回元素的個數
count_if() 返回滿足某一條件的元素的個數
min_element() 返回最小值
max_element() 返回最大值
minmax_element() 返回最小值和最大值元素(始於C++11)
find() 查詢與目標值相等的第一個元素
find_if() 查詢滿足某一條件的第一個元素
find_if_not() 查詢不滿足某一個條件的第一個元素
search_n() 查詢滿足某特性的前n個元素
search() 查詢某個子區間第一次出現的位置
find_end() 查詢某個子區間最後一次出現的位置
find_first_of 查詢數個可能元素中出現的第一個
adjacent_find() 查詢連續兩個相等的(或者滿足某個條件)的元素
equal() 判斷兩個區間是否相等
is_permutation() 判斷兩個不定序區間是否內含相等的元素 (始於C++11)
mismatch() 返回兩序列的各組對應值元素中第一對不相等的元素
lexicographical_compare() 判斷在“字典序”下某序列是否小於另一序列
is_sorted() 返回區間內是否已有序(始於C++11)
is_sorted_until() 返回區間內第一個亂序元素
is_patitioned() 返回“區間內的元素是否基於某準則被分割為兩組”(始於C++11)
partition_point() 返回區間內的一個分割元素,它把元素分為兩個組,其中第一個組滿足predicate,另一個組則不然(始於C++11)
is_heap() 返回“區間內的元素是否形成一個heap”(始於C++11)
is_heap_until() 返回“區間內第一為滿足heap排序準則的元素”(始於C++11)
all_of() 返回“是否所有元素都滿足某準則”(始於C++11)
any_of() 返回“是否存在元素滿足某準則”(始於C++11)
none_of() 返回“是否無任何元素滿足某準則”(始於C++11)

從列表中可以看出,STL中用了find和search兩個單詞來表示命名查詢函式,看起來十分混亂。並且,他們不是分別用來表示查詢元素和查詢區間,而是混合使用了find和search。這使得記憶和使用十分的不便。

還有一點值得注意的是,由於string class和STL class是分開設計的,因此一些函式並不是通用的,下面列出了兩者的區別:

函式作用 String STL
查詢某元素第一次出現的位置 find() find()
查詢某元素最後一次出現的位置 rfind() find() (利用reverse_iterator)
查詢某個子區間第一次出現的位置 find() search()
查詢某個子區間最後一次出現的位置 rfind() find_end()
查詢某數個可能元素第一次出現的位置 find_first_of() find_first_of()
查詢某數個可能元素最後一次出現的位置 find_last_of() find_first_of() (利用reverse_iterator)
n個連續元素第一次出現的位置 / search_n()

由於STL中演算法眾多,不可能全部進行討論,下面挑選了幾類常用的演算法進行討論,其實遵循STL標準設計的演算法都是類似的。

1. 計數
計數演算法為count()和count_if(),二者的函式宣告如下:
count(const _InIt _First, const _InIt _Last, const _Ty& _Val)
count_if(_InIt _First, _InIt _Last, _Pr _Pred)
其中前兩個引數為輸入迭代器,count_if()的第三個引數指定規則。
另外,它們的返回值都是difference_type,是用以表現iterator間距的型別。

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
template<typename T>
void print_container(const T &container);
int main() {
	int num;
	vector<int> vec = { 3, 2, 4, 4, 1, 5, 8, 2, 4 };
	print_container(vec);
	num=count(vec.begin(), vec.end(), 4);//count
	cout << "N(element=4): " << num << endl;
	num = count_if(vec.begin(), vec.end(),
				   [](int elem) {return elem > 4; });//count_if
	cout << "N(element>4): " << num << endl;
	num = count_if(vec.begin(), vec.end(),
		           [](int elem) {return elem % 4 == 0; });//count_if
	cout << "N(element%4=0): " << num << endl;
	return 0;
}
Element: 3 2 4 4 1 5 8 2 4
N(element=4): 3
N(element>4): 2
N(element%4=0): 4

注意這裡count_if()函式的第三個引數lambda函式。

2. 最值
查詢最值的函式有三個:max_element()、min_element()和minmax_element,它們分別用於查詢最大、最小和最大最小值。三者的函式宣告如下:
_FwdIt max_element(_FwdIt _First, _FwdIt _Last)
_FwdIt max_element(_FwdIt _First, _FwdIt _Last, _Pr _Pred)
_FwdIt min_element(_FwdIt _First, _FwdIt _Last)
_FwdIt min_element(_FwdIt _First, _FwdIt _Last, _Pr _Pred)
pair<_FwdIt, _FwdIt> minmax_element(_FwdIt _First, _FwdIt _Last)
pair<_FwdIt, _FwdIt> minmax_element(_FwdIt _First, _FwdIt _Last, _Pr _Pred)
它們預設是以"<"運算子符為比較法則,如果我們想指定比較法則可通過設定第三引數來達到目的,下面舉例說明:

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
template<typename T>
void print_container(const T &container);
int main() {
	int num;
	vector<int> vec = { 3, 2, -4, 4, -1, 5, -8, 2, 4 };
	print_container(vec);
	//最大值
	num = *max_element(vec.begin(), vec.end());
	cout << "Max of value: " << num << endl;
	//絕對值最大值
	num = *max_element(vec.begin(), vec.end(),
				   [](int elem1, int elem2) {return abs(elem1) < abs(elem2); });//count_if
	cout << "Max of absolute value: " << num << endl;
	//最小最大值
	auto nums = minmax_element(vec.begin(), vec.end());
	cout << "Minmax of value : " << *nums.first << "," << *nums.second << endl;

		return 0;
}
Element: 3 2 -4 4 -1 5 -8 2 4
Max of value: 5
Max of absolute value: -8
Minmax of value : -8,5

程式中依次查詢序列中的最大值、絕對值的最大值以及最大最小值。
注意:這裡minmax_element()函式的返回型別為pair型別。

3. 查詢
查詢分為查詢元素查詢區間

查詢元素有關的演算法如下:
_InIt find(_InIt _First, const _InIt _Last, const _Ty& _Val)
_InIt find_if(_InIt _First, const _InIt _Last, _Pr _Pred)
_InIt find_if_not(_InIt _First, const _InIt _Last, _Pr _Pred)
_FwdIt search_n(const _FwdIt _First, const _FwdIt _Last, const _Diff _Count, const _Ty& _Val)
_FwdIt search_n(_FwdIt _First, const _FwdIt _Last,const _Diff _Count_raw, const _Ty& _Val, _Pr _Pred)

1.三個find函式用法

int main() {
	vector<int>::iterator it;
	vector<int> vec = { 4, -2, -4, 4, 4, -1, 5, -8, 2, 4 };
	print_container(vec);
	//查詢4(find)
	it = find(vec.begin(), vec.end(),4);
	if(it!=vec.end())
		cout << "Find value: " << *it << endl;
	//查詢第一個大於4的元素(find_if)
	/*it = find_if(vec.begin(), vec.end(),
		bind(greater<int>(), _1, 4));
	if (it != vec.end())
		cout << "Find value>4: " << *it << endl;
	*/
	//查詢第一個大於4的元素(find_if)
	it = find_if(vec.begin(), vec.end(),
		[](int val) {return val > 4; });
	if (it != vec.end())
		cout << "Find value>4: " << *it << endl;
	
	//查詢第一個小於等於4的元素(find_if_not)
	it = find_if_not(vec.begin(), vec.end(),
		[](int val) {return val > 4; });
	if (it != vec.end())
		cout << "Find value<=4: " << *it << endl;
	return 0;
Element: 4 -2 -4 4 4 -1 5 -8 2 4
Find value: 4
Find value>4: 5
Find value<=4: 4

2.search_n用法:

int main() {
	vector<int>::iterator it;
	vector<int> vec = { 4, -2, -4, 4, 4, -1, 5, -8, 2, 4 };
	print_container(vec);
	it = search_n(vec.begin(), vec.end(),
		2,
		4);
	if (it != vec.end()) {
		cout << "Find 2 of 4 : " << *(it++) << " ";;
		cout << *it << endl;
	}

	it = search_n(vec.begin(), vec.end(),
				  2,
		          0,
		[](int elem,int val) {return elem < 0; });
	if (it != vec.end()) {
		cout << "Find 2 of negative val : " << *(it++) << " ";;
		cout << *it << endl;
	}
	return 0;
}
Element: 4 -2 -4 4 4 -1 5 -8 2 4
Find 2 of 4 : 4 4
Find 2 of negative val : -2 -4

注意,由於查詢不一定成功,所以需在判斷返回的迭代器是否有效:
if (it != vec.end()) {}

查詢區間有關的演算法如下:
_FwdItHaystack search(const _FwdItHaystack _First1, const _FwdItHaystack _Last1,const _FwdItPat _First2, const _FwdItPat _Last2)
_FwdItHaystack search(_FwdItHaystack _First1, const _FwdItHaystack _Last1,const _FwdItPat _First2, const _FwdItPat _Last2, _Pr _Pred)
_FwdIt1 find_end(_FwdIt1 const _First1, const _FwdIt1 _Last1,const _FwdIt2 _First2, const _FwdIt2 _Last2)
_FwdIt1 find_end(_FwdIt1 const _First1, const _FwdIt1 _Last1,const _FwdIt2 _First2, const _FwdIt2 _Last2,_Pr _Pred)

int main() {
	vector<int>::iterator it;
	vector<int> vec = { 4, -2, -4, 4, 4, -1, 5, 4, 4, -1, 5 };
	vector<int> vec1 = { 4, 4, -1, 5 };
	print_container(vec);
	print_container(vec1);
	it = search(vec.begin(), vec.end(), vec1.begin(), vec1.end());
	if (it != vec.end())
		cout << "find vec1 in vec!" << endl;
	it = search(vec.begin(), ++it, vec1.begin(), vec1.end());
	if (it != vec.end())
		cout << "find vec1 in vec again!" << endl;
	return 0;
}
Element: 4 -2 -4 4 4 -1 5 4 4 -1 5
Element: 4 4 -1 5
find vec1 in vec!
find vec1 in vec again!

4. 區間比較
關於區間比較的函式如下:
bool equal(const _InIt1 _First1, const _InIt1 _Last1, const _InIt2 _First2)
bool equal(const _InIt1 _First1, const _InIt1 _Last1, const _InIt2 _First2, _Pr _Pred)
區間比較可以比較某個區間內兩容器元素是否相等或者滿足某一個條件(如互為相反數)。
下面是示例:

int main() {
	bool b;
	vector<int> vec = { 4, -2, -4, 4, 4, -1, 5, 4, 4, -1, 5 };
	vector<int> vec1 = { -4, 2, 4, -4 };
	print_container(vec);
	print_container(vec1);
	b = equal(vec1.begin(), vec1.end(), vec.begin());
	if (b) cout << "true" << endl;
	else cout << "false" << endl;
	b = equal(vec1.begin(), vec1.end(), vec.begin(),
		      [](int elem1, int elem2) {return (elem1 + elem2) == 0; });
	if (b) cout << "true" << endl;
	else cout << "false" << endl;
	return 0;
}
Element: 4 -2 -4 4 4 -1 5 4 4 -1 5
Element: -4 2 4 -4
false
true

上例比較了vec1和vec的前四個元素,第一次比較對應的四個元素是否相等,而第二次比較對應的四個元素是否為相反數。得到的結果為該區間四個元素不等,但它們互為相反數。

上面的equal函式對區間內對應元素進行比較,而is_permutation()函式對區間內函式進行比較,而順序無所謂。
該函式宣告如下:
bool is_permutation(_FwdIt1 _First1, _FwdIt1 _Last1,_FwdIt2 _First2)
bool is_permutation(_FwdIt1 _First1, _FwdIt1 _Last1,_FwdIt2 _First2, _Pr _Pred)

int main() {
	bool b;
	vector<int> vec = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
	vector<int> vec1 = { 2, 1, 4, 3 };
	print_container(vec);
	print_container(vec1);
	b = is_permutation(vec1.begin(), vec1.end(), vec.begin());
	if (b) cout << "true" << endl;
	else cout << "false" << endl;
	return 0;
}
Element: 1 2 3 4 5 6 7 8 9
Element: 2 1 4 3
true

從上例中可以看出,兩個區間的對應元素不等,但元素卻不定序相等,這時也判斷為相等。

5. 有序性判斷
bool is_sorted(_FwdIt _First, _FwdIt _Last)
bool is_sorted(_FwdIt _First, _FwdIt _Last, _Pr _Pred)

int main() {
	bool b;
	vector<int> vec0 = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
	map<int, string> map0 = { {1, "Michael"}, {2,"Alex"}, {3, "Stark"} };
	b = is_sorted(vec0.begin(), vec0.end());
	if (b) cout << "true" << endl;
	else cout << "false" << endl; 
	b = is_sorted(map0.begin(), map0.end(),
		[](pair<int, string>elem1, pair<int, string>elem2) {
			return elem1.first < elem1.first; 
		});
	if (b) cout << "true" << endl;
	else cout << "false" << endl;
	return 0;
}

6. 存在性判斷
bool any_of(const _InIt _First, const _InIt _Last, _Pr _Pred)
bool all_of(_InIt _First, _InIt _Last, _Pr _Pred)
bool none_of(const _InIt _First, const _InIt _Last, _Pr _Pred)
判斷容器中存在/全為/不含滿足某條件的元素。

int main() {
	bool b;
	vector<int> vec = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
	print_container(vec);
	//存在
	b = any_of(vec.begin(), vec.end(),
		[](int elem) {return elem % 5 == 0; });
	if (b) cout << "true" << endl;
	else cout << "false" << endl;
	//全為
	b = all_of(vec.begin(), vec.end(),
		[](int elem) {return elem % 5 == 0; });
	if (b) cout << "true" << endl;
	else cout << "false" << endl;
	//不含
	b = none_of(vec.begin(), vec.end(),
		[](int elem) {return elem > 10; });
	if (b) cout << "true" << endl;
	else cout << "false" << endl;
	return 0;
}
Element: 1 2 3 4 5 6 7 8 9
true
false
true

更易型演算法

非更易型演算法會對容器區間的內容造成改變。包括利用迭代器進行遍歷的過程中造成的改變和將元素從源區間複製到目標區間的過程中造成的改變。

下面列出STL中的更易型演算法:

函式 作用
copy() 從首元素開始,複製某個區間
copy_if() 複製某個區間內滿足某個條件的元素
copy_n() 複製n個元素(始於C++11)
copy_backward() 從最後一個元素開始,複製某個區間
move() 從首元素開始,搬移某個區間(始於C++11)
move_backward() 從最後一個元素開始,搬移某個區間
transform() 改動(並複製)元素,將兩個區間的元素合併
merge() 合併兩個區間
swap_ranges() 交換兩個區間的內容
fill() 以給定值填充區間所有元素
fill_n() 以給定值填充區間中n個元素
generate 以某項操作的結果替換每一個元素
generate_n() 以某項操作的結果替換n個元素
iota() 將所有元素以一些列的遞增值取代(始於C++11)
replace() 將具有某特定值的元素替換為另一個值
replace_if() 將滿足某條件的元素替換為另一個值
replace_copy() 複製整個區間,並將具有某特定值的元素替換為另一個值
replace_copy_if() 複製整個區間,將滿足某條件的元素替換為另一個值

下面進行簡單的介紹:

1. 複製
_OutIt copy(_InIt _First, _InIt _Last, _OutIt _Dest)
_OutIt copy_if(_InIt _First, _InIt _Last, _OutIt _Dest, _Pr _Pred)
_OutIt copy_n(_SourceTy (&_First)[_SourceSize], _Diff _Count_raw, _OutIt _Dest)
注意上述三個函式再呼叫時需確保目標區間由足夠的空間,否則就需要使用insert iterator。

int main() {
	vector<int> vec = { 1, 2, 3, 4, 5, 6, 7, 8