容器演算法
<algorithm>
是c++
自帶的容器演算法,提供一系列實用的演算法。在談到容器演算法,我們大機率會用到謂詞predicate
,謂詞返回的型別是布林型別(bool)
可以是lambda
表示式、函式物件以及其它可呼叫的物件。
查詢
-
find()
查詢元素find
接受三個引數,第三個引數是值型別,set
、map
自帶count
函式也能實現這樣的功能,返回值0
表示不存在。為了方便本次連帶find_if
、find_if_not
、find_first_of
、find_end
和adjacent_find
一起舉例。vector<int> a{1, 1, 2, 3, 4, 4, 5}; vector<int> b{5, 6, 6}; find(a.begin(), a.end(), 3); // 返回的是迭代器,未查詢到返回a.end() find_if(a.begin(), a.end(), [](auto x){return x != 3;}); //接受一個一元謂詞,找到第一個滿足條件的元素 find_if_not(a.begin(), a.end(), [](auto x){return x != 3;}); //接受一個一元謂詞,找到第一個不滿足條件的元素 // find_first_of找到存在於第一個範圍的第二個範圍中的第一個元素(可能並不是第二個範圍第一個元素)返回迭代器,也支援一個二元謂詞。 vector<int> a{1, 1, 2, 3, 4, 4, 5}; vector<int> b{6, 5, 6}; auto it = find_first_of(a.begin(), a.end(), b.begin(), b.end()); cout << *it << endl; // 返回5 // find_end找到最後一個匹配子序列的位置,返回最後一個子序列開始的迭代器,也支援一個二元謂詞 vector<int> a{1, 1, 2, 3, 1, 4, 1, 2}; vector<int> b{1, 2}; auto it = find_end(a.begin(), a.end(), b.begin(), b.end()); cout << distance(a.begin(), it) << endl; // 6指向第7個元素 // adjcent_find找到兩個值鄰近的元素,返回指向找到的第一個元素的迭代器,接受一個二元謂詞 vector<int> a{1, 1, 2, 3, 4, 1, 2}; vector<int> b{1, 2}; auto it = adjacent_find(a.begin(), a.end()); auto it = adjacent_find(a.begin(), a.end(), [](auto a, auto b)->bool{return a + b = 10;}); cout << *it << endl;
去重
-
unique(nums.begin(), nums.end())
除掉連續相同的值unique
函式的作用是刪除掉的連續的相同的值,unique還支援傳入二元謂詞(可以理解為一個引數的函式),會返回一個迭代器it
,但是並不是end()
,中間是未指定的值(訪問可能會產生未定義行為)。注意不要用set
和map
,返回的都是常量迭代器是無法改變的,即其指向的元素無法改變。// 無謂詞寫法 vector<int> a{1, 1, 2, 3, 4, 4, 5}; auto it = unique(a.begin(), a.end()); // 此時a為{1, 2, 3, 4, 5} for_each(a.begin(), a.end(), [](auto it){cout << it << endl;}); // 輸出為{1, 2, 3, 4, 5, 4, 5} for_each(a.begin(), it, [](auto it){cout << it << endl;}); // 輸出為{1, 2, 3, 4, 5} // 有二元謂詞 vector<int> a{1, 1, 2, 3, 4, 4, 5}; auto it = unique(a.begin(), a.end(), [](auto a, auto b) -> bool{return a != b;}); // 此時a為{1, 2, 3, 4, 5},lambda表示式作為一個二元謂詞,每次從陣列中取兩個嚴肅進行判斷,然後刪除不相等的元素。 for_each(a.begin(), a.end(), [](auto it) -> int{cout << it << endl;}); // Error,這是因為unique將返回的迭代器到之後的end()迭代器指向的值刪除了,但是空間還在。此時訪問,會發生未定義行為。 for_each(a.begin(), it, [](auto xs){cout << xs << endl;}); // 1,1 cout << distance(a.begin(), a.end()) << endl; // 7,distance求算距離是7
-
unique_copy()
除掉連續相同的值並複製到目標容器注意,使用此函式之前先分配空間,再使用
unique_copy
並不進行記憶體分配,只是賦值給另一個容器,如果訪問未賦值部分的容器區域,那麼會產生未定義行為。同樣,也支援二元謂詞,透過二元謂詞判定移除連續值。vector<int> a{1, 1, 2, 3, 4, 4, 5}; vector<int> b(7, 0); auto it = unique_copy(a.begin(), a.end(), b.begin()); for_each(b.begin(), it, [](auto xs){cout << xs << endl;}); // out: 1 2 3 4 5 // 二元謂詞, 一般不要使用二元謂詞與數字直接進行比較,可以使用remove實現 auto it = unique_copy(a.begin(), a.end(), b.begin(), [x](auto a, auto b) -> bool{return a == 3;}); for_each(b.begin(), it, [](auto xs){cout << xs << endl;}); // out: 1 1 2 3,解釋值等於3才會進行unique_copy auto it = unique_copy(a.begin(), a.end(), b.begin(), [x](auto a, auto b) -> bool{return a == b;}); for_each(b.begin(), it, [](auto xs){cout << xs << endl;}); // out: 1 2 3 4 5,解釋a = b,才會進行復制
排序
-
sort()
對容器進行排序sort
用於實現容器元素的排序,sort也同樣接受二元謂詞sort(a.begin(), a.end()); sort(a.begin(), a.end(), greater<>()); // 使用庫中自帶的greater\less物件
迭代器差值
-
distance()
求迭代之間距離distance
用於求算兩個迭代器之間的差值,只用於同一容器,set
,map
,vector
都可以用。distance(c.begin(), c.end());
遍歷容器
-
for_each()
和for_each_n
遍歷容器每個元素並執行函式規定的操作,第三個引數是一個函式。如果要對迭代器指向的元素修改,那麼set
就是不可以的,因為set
返回的是常量迭代器。array<int, 5> a{1, 2, 3, 4, 5}; for_each(a.begin(), a.end(), [](int& x){cout<< x; x *= 2;}); // 此時元素值會發生改變,因為捕獲的是引用。 for_each(a.begin(), a.end(), [](auto x) -> void{x *= 2; cout << x;}); // 此時元素值不會發生改變,捕獲的是值,會變為副本。 for_each_n(a.begin, 5, [](auto x) -> void{x *= 2; cout << x;}); // 第二個引數指定操作的數量n, 對n個元素進行操作。
複製元素
-
copy()
能夠將容器中的元素複製到輸出迭代器之後out iterator
vector<vector<int>> sx(1); // 建議複製前先分配多個空間,避免不必要的擴容 copy(sx.begin(), sx.end() , sx.begin()); // 可以將[begin, end)之間的元素複製到從begin()開始的位置。這種操作會將原先的元素覆蓋一次 // 一個有意思的做法是使用back_inserter指明要進行尾插法,某些支援雙向插入的容器也可以轉變未尾插容器,省去了一些需要選擇插入函式的麻煩。 // set、map類紅黑樹容器不存在後插方法,無法進行此類呼叫,注意 copy(sx.begin(), sx.end() , back_inserter(sx)); // 負值時會在末尾插入元素 vector<int> a{1, 2, 3}; set<int> s; copy(a.begin(), a.end() , back_inserter(s)); // Error // 接下來我們考慮一個向vector<vector<int>>複製的問題 vector<vector<int>> v(1); // 此時外部vector容量為1,size也是1。說明內部存在一個vector容器元素,但是此時vector[0]的size和capacity都是0,是一個空容器。 copy(v.begin(), v.end(), back_inserter(v)); // 此時內部空容器是2個
-
copy_backward()
能夠將容器中元素複製到輸出迭代器之前。map、set
類也不支援。這個容器演算法會覆蓋掉輸出迭代器之前的元素vector<int> a{1, 2, 3, 4, 5, 6}; set<int> s; copy_backward(a.begin(), a.end() , s); // Error copy_backward(a.begin(), a.begin() + 3, a.end()); // OK a{1, 2, 3, 1, 2, 3} copy_backward(a.begin(), a.begin() + 3, a.begin()); // OK,但是陣列不會發生任何改變,相當於複製到begin()迭代器之前沒有意義。
全排列
next_permutation
提供返回容器的一個全排列,也支援一個二元謂詞。
next_permutation(a.begin(), a.end());
取集合
set_intersection
取交集、set_union
取並集、set_difference
取單側差集,set_symmetric_difference
取兩側差集,值得注意的是set_difference
在輸出到結果容器中只會輸出第一個容器中的元素(不管第一個容器的元素是否少於第二個容器的元素)。這四個函式都接受一個二元謂詞。注意:map
和set
都無法使用,因為這三個函式底層實現都需要用到自增運算子,而map和set只有常量迭代器不支援這樣的操作。應用場景:vector
等容器。
vector<int> a{1, 2, 2, 6, 7, 9};
vector<int> b{1, 2, 5, 8, 10};
vector<int> interResult;
vector<int> unionResult;
vector<int> differenceResult;
vector<int> symmetricResult;
set_intersection(a.begin(), a.end(),b.begin(), b.end(), back_inserter(interResult));
set_union(a.begin(), a.end(),b.begin(), b.end(), back_inserter(unionResult));
set_difference(a.begin(), a.end(),b.begin(), b.end(), back_inserter(differenceResult));
set_symmetric_difference(a.begin(), a.end(),b.begin(), b.end(), back_inserter(symmetricResult));
cout << "intersection:" <<endl;
for_each(interResult.begin(), interResult.end(), [](auto& item){cout << item << ":";});
cout << endl;
cout << "differencesection:" <<endl;
for_each(differenceResult.begin(), differenceResult.end(), [](auto& item){cout << item << ":";});
cout << endl;
cout << "unionsection:" <<endl;
for_each(unionResult.begin(), unionResult.end(), [](auto& item){cout << item << ":";});
cout << endl;
cout << "symmetric_diff_section:" <<endl;
for_each(symmetricDifferenceResult.begin(), symmetricDifferenceResult.end(), [](auto& item){cout << item << ":";});
/* output
* intersection:
* 1:2:
* differencesection:
* 2:6:7:9:
* unionsection:
* 1:2:2:5:6:7:8:9:10:
* symmetric_diff_section:
* 2:6:7:9:5:8:10 */
搜尋
search
搜尋串中的子序列,search_n
搜尋串中固定數量的連續重複值。search_n
接受一個二元謂詞,兩個函式都能夠使用set
和map
。search
接受一個searcher
可呼叫物件,但是search_n
對於set
是沒有意義的,因為set
中不存在重複值
vector<int> a{1,1,1,4};
vector<int> b{1,2};
auto it = search(a.begin(), a.end(),b.begin(), b.end());
auto xs = search_n(a.begin(), a.end(), 3, 1);
int ret = it == a.end()? -1 : 1;
int res = xs == a.end()? -1 : 1;
cout << ret <<endl; // -1; 未找到
cout << res << endl; // 1; 找到了{1,1,1}