C++學習-入門到精通【14】標準庫算法

C++學習-入門到精通【14】標準庫算法


目錄

  • C++學習-入門到精通【14】標準庫算法
    • 一、對迭代器的最低要求
      • 迭代器無效
    • 二、算法
      • 1.fill、fill_n、generate和generate_n
      • 2.equal、mismatch和lexicographical_compare
      • 3.remove、remove_if、remove_copy和remove_copy_if
      • 4.replace、replace_if、replace_copy和replace_copy_if
      • 5.數學算法
      • 6.基本查找和排序算法
      • 7.swap、iter_swap和swap_ranges
      • 8.copy_backward、merge、unique和reverse
      • 9.inplace_merge、unique_copy和reverse_copy
      • 10.集合操作
      • 11.lower_bound、upper_bound和equal_range
      • 12.堆排序
      • 13.min、max、minmax和minmax_element
    • 三、函數對象
      • 函數對象較函數指針的優越之處
      • 標準模板庫中預先定義的函數對象
      • 使用標準庫的accumulate算法
    • 四、lambda表達式
    • 五、標準庫算法總結


一、對迭代器的最低要求

通常標準庫會將算法和容器分離,這樣做是為了使添加新算法更容易。每個容器的一個重要部分就是它支持的迭代器類型,這決定了該容器能夠使用什么樣的算法。所以每個以迭代器為參數的算法的標準庫算法,都要求迭代器提供最低限度的功能。例如,如果一個算法需要前向迭代器,那么它就可以使用在支持任何支持前向迭代器、雙向迭代器和隨機訪問迭代器的容器中。

標準庫算法并不依賴它們操作的容器的實現細節,只要該容器的迭代器滿足算法的需求,那么這個算法就能作用在這個容器上。

在性能允許的情況下使用“最弱的迭代器”,這將有助于產生可用性最大化的組件。例如,一個算法只需要前向迭代器,那么它就可以與支持前向迭代器、雙向迭代器或隨機訪問迭代器的任何容器一起使用,但是如果一個算法需要隨機訪問迭代器,那么它只能與支持隨機訪問迭代器的容器一起使用。

迭代器無效

迭代器只是簡單地指向容器的元素,因此當對容器進行某些修改時,迭代器就有可能會失效。例如,如果對一個vector對象調用clear函數,那么會間接調用erase函數,將所有元素全部刪除。如果在調用clear函數之前,程序中有任何指向該vector對象中元素的迭代器,那么現在這些迭代器就會失效。下面我們總結了在插入和刪除操作期間迭代器何時會失效:

插入操作:

  • vector —— 如果 vector 對象被重新分配,那么所有指向這個 vector 對象的迭代器都將變得無效。如果沒有重新分配,那么從插入點到這個對象末尾的迭代器都將變為無效(所有元素都會后移);
  • deque —— 所有迭代器都變為無效;deque通常實現為多個固定大小的數組(被稱為塊或緩沖區)的集合,這些塊由一個中央控制結構(通常是一個數組,被稱為映射器)管理,映射器存儲指向這些塊的指針。所以一個指向deque對象元素的迭代器中通常包含多個組件:指向當前元素的指針、指向當前內存塊起始位置的指針、指向當前內存塊結束位置的指針和指向映射數組中管理當前內存塊的指針的指針。當進行插入操作時觸發了映射數組的擴容和重新分配,那么映射數組就移動到了一個新的地址,之前所有迭代器中存儲的指向映射數組或其內部指針的指針都變得無效了,即使迭代器之前指向的元素的地址沒有發生變化,迭代器也無法再正確地定位它所在的塊,所以迭代器會失效。由于用戶是無法預知哪一次操作會導致上面情況的發生,所以C++標準規定,當對一個deque對象進行了一次插入之后,之前的所有指向該對象的迭代器都會失效。
  • list 或 forward_list —— 所有迭代器都依然有效。它們本身實現就是鏈表,不管如何插入元素,迭代器都還是會指向原來指向的元素。
  • 有序的關聯容器 —— 所有迭代器都依然有效。這是因為有序的關聯容器基本上都是使用紅黑樹來實現,而樹結構在插入新節點之后,只需要修改節點中的父子指針的指向即可(進行旋轉),節點本身的物理地址是沒有發生變化的,所以迭代器不會失效。
  • 無序的關聯容器 —— 所有迭代器都會失效。無序的關聯容器使用哈希表來實現,它的內存分布是由一個桶數組(一個連續的內存塊)來存儲表頭指針或是動態數組(存儲一個數組其實就是存儲該數組首元素的地址)和用于解決沖突的鏈表或動態數組組成。當進行插入操作后,可能會導致桶數組重新分配,此時桶數組的地址會發生變化,迭代器內會存儲桶數組的地址用來尋址,所以迭代器會失效。所以保守起見,C++標準庫中規定,只要對一個無序關聯容器插入新元素,那么所有的迭代器都會失效;

刪除操作:當對容器進行刪除操作時,指向被刪除元素的迭代器會失效。

  • vector —— 從被刪除元素到這個 vector 對象末尾的迭代器都會變為無效;
  • deque —— 如果處在這個 deque 對象中部的元素被刪除,那么所有的迭代器都會變為無效;

迭代器本質上是對底層內存地址的抽象封裝。它存儲的是一個指向特定內存位置的指針(或者類似指針的東西,當進行插入或刪除操作后,原來的元素可能會在內存發生移動,這里再使用原來的迭代器就無法找到對應的元素。這就相當于是你的朋友搬家之后,你仍使用他原來的住址去找他,顯然是不合適的)。

二、算法

1.fill、fill_n、generate和generate_n

下面的程序演示了標準庫中的fillfill_ngenerategenerate_n算法。

fillfill_n 算法把容器中某個范圍內的元素設置為一個指定值。

在這里插入圖片描述

在這里插入圖片描述

generategenerate_n 算法使用一個生成函數為容器中一定范圍內的元素創建新的值。

在這里插入圖片描述

在這里插入圖片描述

test.cpp

#include <iostream>
#include <algorithm>
#include <array>
#include <iterator>
using namespace std;// 元素生成函數
char nextLetter();int main()
{array<char, 10> chars;ostream_iterator<char> output(cout, " ");fill(chars.begin(), chars.end(), '6'); // 為chars中begin()和end()之間的元素全部分配6cout << "chars after filling with 6:\n";copy(chars.cbegin(), chars.cend(), output);fill_n(chars.begin(), 5, '8'); // 為chars的前5個元素重新分配值為8cout << "\n\nchars after filling five elements with 8:\n";copy(chars.cbegin(), chars.cend(), output);generate(chars.begin(), chars.end(), nextLetter);cout << "\n\nchars after generate letters A-J:\n";copy(chars.cbegin(), chars.cend(), output);// 這里是使用了函數指針generate_n(chars.begin(), 5, nextLetter);cout << "\n\nchars after generate letters K-O:\n";copy(chars.cbegin(), chars.cend(), output);
}char nextLetter()
{static char letter = 'A';return letter++;
}

運行結果:

在這里插入圖片描述

2.equal、mismatch和lexicographical_compare

在這里插入圖片描述

在這里插入圖片描述

在這里插入圖片描述

示例代碼:

#include <iostream>
#include <array>
#include <algorithm>
#include <iterator>
using namespace std;int main()
{const size_t SIZE = 10;array <int, SIZE> a1 { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };array <int, SIZE> a2(a1); // 使用拷貝構造函數創建一個array對象array <int, SIZE> a3 { 1, 2, 3, 4, 10000, 6, 7, 8, 9, 10 };ostream_iterator<int> output(cout, " ");cout << "a1 contains: ";copy(a1.cbegin(), a1.cend(), output);cout << "\na2 contains: ";copy(a2.cbegin(), a2.cend(), output);cout << "\na3 contains: ";copy(a3.cbegin(), a3.cend(), output);// 比較a1和a2是否相等bool result = equal(a1.cbegin(), a1.cend(), a2.cbegin(), a2.cend());cout << "\n\na1 " << (result ? "is " : "is not ") << "equal to a2.\n";// 比較a1和a3是否相等result = equal(a1.cbegin(), a1.cend(), a3.cbegin(), a3.cend());cout << "a1 " << (result ? "is ": "is not ") << "equal to a3.\n";auto location = mismatch(a1.cbegin(), a1.cend(), a3.cbegin(), a3.cend());cout << "\nThere is a mismatch between a1 and a3 at location "<< (location.first - a1.cbegin()) << "\nwhere a1 contains "<< *location.first << " and a3 contains " << *location.second << "\n\n";char c1[SIZE] = "HELLO";char c2[SIZE] = "BYE BYE";result = lexicographical_compare(cbegin(c1), cend(c1), cbegin(c2), cend(c2));cout << c1 << (result ? " < " : " >= ") << c2 << endl;
}

運行結果:

在這里插入圖片描述

3.remove、remove_if、remove_copy和remove_copy_if

在這里插入圖片描述
在這里插入圖片描述
注意上面的算法不會改變容器的容量,也并不會真的將刪除元素,只是用與參數指定值相同的元素使用它后一個元素替換。

示例代碼:

#include <iostream>
#include <algorithm>
#include <array>
#include <iterator>
using namespace std;bool greater9(int);int main()
{const size_t SIZE = 10;array<int, SIZE> init = { 10, 2, 10, 4, 16, 6, 14, 8, 12, 10 };ostream_iterator<int> output(cout, " ");// 測試removecout << "Testing algorithm \"remove\"\n";array<int, SIZE> a1(init);cout << "a1 before remove all 10:\n";copy(a1.cbegin(), a1.cend(), output);auto newLastElement = remove(a1.begin(), a1.end(), 10);cout << "\na1 after remove all 10(newLastElement):\n";copy(a1.begin(), newLastElement, output);cout << "\na1 between a1.cbegin and a1.cend:\n";copy(a1.cbegin(), a1.cend(), output);// 測試remove_copycout << "\n\nTesting algorithm \"remove_copy\"\n";array<int, SIZE> a2(init);array<int, SIZE> a3 { };cout << "a2 before removing all 10 and copying:\n";copy(a2.cbegin(), a2.cend(), output);// 將a2中不是10的元素復制到a3中newLastElement = remove_copy(a2.cbegin(), a2.cend(), a3.begin(), 10);cout << "\na3 after removing all 10 from a2(newLastElement):\n";copy(a3.begin(), newLastElement, output);cout << "\na3 after removing all 10 from a2:\n";copy(a3.cbegin(), a3.cend(), output);// 測試remove_ifcout << "\n\nTesting algorithm \"remove_if\"\n";array<int, SIZE> a4(init);cout << "a4 before removing all elements greater than 9:\n";copy(a4.cbegin(), a4.cend(), output);// 使用remove_if函數,該函數可以指定條件,“刪除”滿足條件的元素newLastElement = remove_if(a4.begin(), a4.end(), greater9);cout << "\na4 after removing all elements greater than 9(newLastElement):\n";copy(a4.begin(), newLastElement, output);cout << "\na4 after removing all elements greater than 9:\n";copy(a4.cbegin(), a4.cend(), output);// 測試remove_copy_ifcout << "\n\nTesting algorithm \"remove_copy_if\"\n";array<int, SIZE> a5(init);array<int, SIZE> a6{};cout << "a5 before removing all elements greater than 9 and copying:\n";copy(a5.cbegin(), a5.cend(), output);newLastElement = remove_copy_if(a5.begin(), a5.end(), a6.begin(), greater9);cout << "\na6 after removing all elements greater than 9(newLastElement):\n";copy(a6.begin(), newLastElement, output);cout << "\na6 after removing all elements greater than 9:\n";copy(a6.cbegin(), a6.cend(), output);cout << endl;
}bool greater9(int x)
{return x > 9;
}

運行結果:

在這里插入圖片描述

從結果我們可以看出上面的這4個版本的算法其實并不會真的將元素刪除,僅僅是將容器中與指定元素不同的元素往前移動,并返回一個指向這些與指定元素不同的最后一個元素的迭代器,在這個迭代器之后的元素是什么并不確定。

與容器中實現的成員函數remove比較,算法remove并不會真正的刪除元素,而成員函數remove會真正的刪除元素。

4.replace、replace_if、replace_copy和replace_copy_if

在這里插入圖片描述

在這里插入圖片描述

示例代碼:

#include <iostream>
#include <array>
#include <iterator>
#include <algorithm>
using namespace std;bool greater9(int);int main()
{const size_t SIZE = 10;array<int, SIZE> init = { 10, 2, 10, 4, 16, 6, 14, 8, 12, 10 };ostream_iterator<int> output(cout, " ");// 測試replacecout << "Testing of algorithm \"replace\"";array<int, SIZE> a1(init);cout << "\na1 before replacing all 10:\n";copy(a1.cbegin(), a1.cend(), output);cout << "\na1 after replacing all 10 with 100:\n";replace(a1.begin(), a1.end(), 10, 100);copy(a1.cbegin(), a1.cend(), output);// 測試replace_copycout << "\n\nTesting of algorithm \"replace_copy\"";array<int, SIZE> a2(init);array<int, SIZE> c1{};cout << "\na2 before replacing all 10 and copying:\n";copy(a2.cbegin(), a2.cend(), output);cout << "\nc1 after replacing all 10 in a2:\n";replace_copy(a2.begin(), a2.end(), c1.begin(), 10, 100);copy(c1.cbegin(), c1.cend(), output);// 測試replace_ifcout << "\n\nTesting of algorithm \"replace_if\"";array<int, SIZE> a3(init);cout << "\na3 before replacing all elements greater than 9:\n";copy(a3.cbegin(), a3.cend(), output);cout << "\na3 after replacing all elements greater than 9 with 100:\n";replace_if(a3.begin(), a3.end(), greater9, 100);copy(a3.cbegin(), a3.cend(), output);// 測試replace_copy_ifcout << "\n\nTesting of algorithm \"replace_copy_if\"";array<int, SIZE> a4(init);array<int, SIZE> c2{};cout << "\na4 before replacing all greater than 9 and copying:\n";copy(a4.cbegin(), a4.cend(), output);cout << "\nc2 after replacing all greater than 9 in a4:\n";replace_copy_if(a4.begin(), a4.end(), c2.begin(), greater9, 100);copy(c2.cbegin(), c2.cend(), output);cout << endl;
}bool greater9(int x)
{return x > 9;
}

運行結果:

在這里插入圖片描述

5.數學算法

下面的程序演示了幾個常用的數學算法,包括:random_shufflecountcount_ifmin_elementmax_elementminmax_elementaccumulatefor_eachtransform

在這里插入圖片描述

在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述

示例代碼:

#include <iostream>
#include <algorithm>
#include <numeric> // accumulate算法定義在該頭文件中
#include <array>
#include <iterator>
using namespace std;bool greater9(int);
void outputSquare(int);
int calculateCube(int);int main()
{const size_t SIZE = 10;array<int, SIZE> a1 = { 1,2,3,4,5,6,7,8,9,10 };ostream_iterator<int> output(cout, " ");// random_shufflecout << "a1 before random_shuffle: ";copy(a1.cbegin(), a1.cend(), output);// 使用默認的rand函數random_shuffle(a1.begin(), a1.end());cout << "\na1 after random_shuffle: ";copy(a1.cbegin(), a1.cend(), output);// count和count_ifarray<int, SIZE> a2 = { 100,2,8,1,50,3,8,8,9,10 };cout << "\n\na2 contains: ";copy(a2.cbegin(), a2.cend(), output);// 計算a2中8的個數auto result = count(a2.cbegin(), a2.cend(), 8);cout << "\nNumber of elements matching 8: " << result;// 計算a2中大于9的元素的個數result = count_if(a2.cbegin(), a2.cend(), greater9);cout << "\nNumber of elements greater than 9: " << result;// min_element、max_element和 minmax_elementcout << "\n\nMinimum element in a2 is: "<< *(min_element(a2.cbegin(), a2.cend()));cout << "\nMaximum element in a2 is: "<< *(max_element(a2.cbegin(), a2.cend()));auto minAndMax = minmax_element(a2.cbegin(), a2.cend());cout << "\nThe minimum and maximum elements in a2 are: "<< *minAndMax.first << " and " << *minAndMax.second << ", respectively.";// accumulatecout << "\n\nThe total of the elements in a1 is: "<< accumulate(a1.cbegin(), a1.cend(), 0);// for_eachcout << "\n\nThe square of every integer in a1 is:\n";for_each(a1.cbegin(), a1.cend(), outputSquare);array<int,SIZE> cubes;// transformtransform(a1.cbegin(), a1.cend(), cubes.begin(), calculateCube);cout << "\n\nThe cube of every integer in a1 is:\n";copy(cubes.cbegin(), cubes.cend(), output);cout << endl;
}bool greater9(int x)
{return x > 9;
}void outputSquare(int value)
{cout << value * value << ' ';
}int calculateCube(int value)
{return value * value * value;
}

運行結果:

在這里插入圖片描述

6.基本查找和排序算法

下面將演示標準庫中的幾種基本的查找和排序算法,包括:findfind_iffind_if_notsortbinary_searchall_of、和any_of

在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述

示例代碼:

#include <iostream>
#include <algorithm>
#include <array>
#include <iterator>
using namespace std;bool greater10(int);
bool less10(int);int main()
{const size_t SIZE = 10;array<int, SIZE> a { 10, 2, 17, 5, 16, 8, 13, 11, 20, 7 };ostream_iterator<int> output(cout, " ");cout << "array a contains: ";copy(a.cbegin(), a.cend(), output);// find、find_if、find_if_notauto location = find(a.cbegin(), a.cend(), 16);if (location != a.cend()) // 范圍內存在元素16{// 顯示第一個值為16的元素在容器中位置cout << "\n\nFound 16 at location " << (location - a.cbegin()); }elsecout << "\n\n16 not found";location = find(a.cbegin(), a.cend(), 100);if (location != a.cend()) // 范圍內存在元素100{// 顯示第一個值為100的元素在容器中位置cout << "\n\nFound 100 at location " << (location - a.cbegin());}elsecout << "\n\n100 not found";// 查找容器內第一個大于10的元素location = find_if(a.cbegin(), a.cend(), greater10);if (location != a.cend()) {// 顯示第一個值大于10的元素在容器中位置cout << "\n\nThe first value greater than 10 is " << *location<< "\nfound at location" << (location - a.cbegin());}elsecout << "\n\nNo values greater than 10 were found.";location = find_if_not(a.cbegin(), a.cend(), less10);if (location != a.cend()){// 顯示第一個值大于10的元素在容器中位置cout << "\n\nThe first value not less than 10 is " << *location<< "\nfound at location" << (location - a.cbegin());}elsecout << "\n\nNo values not less than 10 were found.";// sortsort(a.begin(), a.end());cout << "\n\narray a after sort: ";copy(a.cbegin(), a.cend(), output);// binary_search// 判斷容器中是否存在元素13if(binary_search(a.cbegin(), a.cend(), 13))cout << "\n\n13 was found in array a";elsecout << "\n\n13 was not found in array a";// 判斷容器中是否存在元素13if (binary_search(a.cbegin(), a.cend(), 100))cout << "\n\n100 was found in array a";elsecout << "\n\n100 was not found in array a";// all_of、any_of和none_ofif(all_of(a.cbegin(), a.cend(), greater10))cout << "\n\nAll the elements in array a are greater than 10";elsecout << "\n\nSome elements in array a are not greater than 10";if (any_of(a.cbegin(), a.cend(), greater10))cout << "\n\nSome elements in array a are greater than 10";elsecout << "\n\nNone elements in array a are not greater than 10";if (none_of(a.cbegin(), a.cend(), greater10))cout << "\n\nNone elements in array a are greater than 10";elsecout << "\n\nSome elements in array a are not greater than 10";cout << endl;
}bool greater10(int value)
{return value > 10;
}// value 不超過 10 時返回 true
bool less10(int value)
{return value <= 10;
}

運行結果:

在這里插入圖片描述

7.swap、iter_swap和swap_ranges

在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述

注意,在使用swap_ranges時,交換的兩個區域不能重疊。

示例代碼:

#include <iostream>
#include <iterator>
#include <array>
#include <algorithm>
using namespace std;int main()
{const size_t SIZE = 10;array<int, SIZE> a = { 1,2,3,4,5,6,7,8,9,10 };ostream_iterator<int> output(cout, " ");cout << "Array a contains:\n  ";copy(a.cbegin(), a.cend(), output);swap(a[0], a[5]);cout << "\nArray a after swapping a[0] and a[5] using swap:\n  ";copy(a.cbegin(), a.cend(), output);iter_swap(a.begin(), a.begin() + 5);cout << "\nArray a after swapping a[0] and a[5] using iter_swap:\n  ";copy(a.cbegin(), a.cend(), output);swap_ranges(a.begin(), a.begin() + 5, a.begin() + 5);cout << "\nArray a after swapping the first five elements with the last five elements:\n  ";copy(a.cbegin(), a.cend(), output);cout << endl;
}

運行結果:

在這里插入圖片描述

8.copy_backward、merge、unique和reverse

在這里插入圖片描述

在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述

示例代碼:

#include <iostream>
#include <algorithm>
#include <iterator>
#include <array>
using namespace std;int main()
{const size_t SIZE = 5;array<int, SIZE> a1 { 1,3,5,7,9 };array<int, SIZE> a2 { 2,4,5,7,9 };ostream_iterator<int> output(cout, " ");cout << "array a1 contains: ";copy(a1.cbegin(), a1.cend(), output);cout << "\narray a2 contains: ";copy(a2.cbegin(), a2.cend(), output);array<int, SIZE> results {};copy_backward(a1.cbegin(), a1.cend(), results.end());cout << "\n\nAfter copy_backward, results contains: ";copy(results.cbegin(), results.cend(), output);array<int, SIZE + SIZE> results2;merge(a1.cbegin(), a1.cend(), a2.cbegin(), a2.cend(), results2.begin());cout << "\n\nAfter merge of a1 and a2, results2 contains: ";copy(results2.cbegin(), results2.cend(), output);auto endLocation = unique(results2.begin(), results2.end());cout << "\n\nAfter unique results2 contains (endLocation): ";copy(results2.begin(), endLocation, output);cout << "\nAfter unique results2 contains: ";copy(results2.cbegin(),results2.cend(), output);reverse(a1.begin(), a1.end());cout << "\n\nArray a1 after reverse: ";copy(a1.cbegin(), a1.cend(), output);cout << endl;
}

運行結果:

在這里插入圖片描述

對于上面程序merge算法的使用,我們提前創建了一個array對象用于接收合并后的數據。如果我們事先并不知道被合并的兩個容器的總的元素個數是多少,我們應該怎么辦呢?使用頭文件<iterator>中定義的back_inserterfront_inserterinserter函數。這些函數會在參數指定的容器中插入元素,它們會返回一個指向將要被插入的位置的迭代器,使用該迭代器進行插入操作。

9.inplace_merge、unique_copy和reverse_copy

在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
注意在使用inplace_merge之后,序列會自動排序

示例代碼:

#include <iostream>
#include <iterator>
#include <algorithm>
#include <array>
#include <vector>
using namespace std;int main()
{const size_t SIZE = 10;array <int, SIZE> a1 = { 1, 3, 5, 7, 9, 1, 3, 5, 7, 9 };ostream_iterator<int> output(cout, " ");cout << "array a1 contains: ";copy(a1.cbegin(), a1.cend(), output);inplace_merge(a1.begin(), a1.begin() + 5, a1.end());cout << "\nAfter inplace_merge, a1 contains: ";copy(a1.cbegin(), a1.cend(), output);vector<int> results1;// 使用 back_inserter 往vector對象中插入新元素unique_copy(a1.cbegin(),a1.cend(), back_inserter(results1));cout << "\nAfter unique_copy, results1 contains: ";copy(results1.cbegin(), results1.cend(), output);vector<int> results2;// 使用 back_inserter 往vector對象中插入新元素reverse_copy(a1.cbegin(), a1.cend(), back_inserter(results2));cout << "\nAfter reverse_copy, results2 contains: ";copy(results2.cbegin(), results2.cend(), output);cout << endl;
}

運行結果:

在這里插入圖片描述

在上面的程序中我們就使用之前提到的back_inserter函數來在vector對象的末端添加新元素。而back_inserter實際上使用vector的push_back函數來在這個vector對象的末端插入新元素,并不是修改已有元素的值。

10.集合操作

標準庫中還有一些用來操作有序值集合的算法,包括:includesset_differenceset_intersectionset_symmetric_differenceset_union

在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述

示例代碼:

#include <iostream>
#include <iterator>
#include <algorithm>
#include <array>
using namespace std;int main()
{const size_t SIZE1 = 10;const size_t SIZE2 = 5;const size_t SIZE3 = 20;array<int, SIZE1> a1 = {1,2,3,4,5,6,7,8,9,10};array<int, SIZE2> a2 = {4,5,6,7,8};array<int, SIZE2> a3 = {4,5,6,11,15};ostream_iterator<int> output(cout, " ");cout << "a1 contains: ";copy(a1.cbegin(), a1.cend(), output);cout << "\na2 contains: ";copy(a2.cbegin(), a2.cend(), output);cout << "\na3 contains: ";copy(a3.cbegin(), a3.cend(), output);// 判斷a2是否是a1的子序列if (includes(a1.cbegin(), a1.cend(), a2.cbegin(), a2.cbegin())){cout << "\n\na1 includes a2";}else{cout << "\n\na1 does not includes a2";}// 判斷a3是否是a1的子序列if (includes(a1.cbegin(), a1.cend(), a3.cbegin(), a3.cbegin())){cout << "\na1 includes a3";}else{cout << "\na1 does not includes a3";}// 測試set_differencearray<int, SIZE1> difference{};auto result1 = set_difference(a1.cbegin(), a1.cend(), a2.cbegin(), a2.cend(), difference.begin());cout << "\n\nset_difference of a1 and a2 is: ";copy(difference.begin(), result1, output);// 測試set_intersectionarray<int, SIZE1> intersection{};auto result2 = set_intersection(a1.cbegin(), a1.cend(),a2.cbegin(), a2.cend(), intersection.begin());cout << "\n\nset_intersection of a1 and a2 is: ";copy(intersection.begin(), result2, output);// 測試set_symmetric_differencearray<int, SIZE1 + SIZE2> symmetric_difference{};auto result3 = set_symmetric_difference(a1.cbegin(), a1.cend(),a3.cbegin(), a3.cend(), symmetric_difference.begin());cout << "\n\nset_symmetric_difference of a1 and a3 is: ";copy(symmetric_difference.begin(), result3, output);// 測試set_unionarray<int, SIZE3> unionSet{};auto result4 = set_union(a1.cbegin(), a1.cend(),a3.cbegin(), a3.cend(), unionSet.begin());cout << "\n\nset_union of a1 and a3 is: ";copy(unionSet.begin(), result4, output);cout << endl;
}

運行結果:

在這里插入圖片描述

11.lower_bound、upper_bound和equal_range

在這里插入圖片描述

在這里插入圖片描述
在這里插入圖片描述

示例代碼:

#include <iostream>
#include <iterator>
#include <array>
#include <algorithm>
using namespace std;int main()
{const size_t SIZE = 10;array<int, SIZE> a = { 2,2,4,4,4,6,6,6,6,8 };ostream_iterator<int> output(cout, " ");cout << "array a contains:\n";copy(a.cbegin(), a.cend(), output);// 找到a中第一個不小于6的元素auto lower = lower_bound(a.cbegin(), a.cend(), 6);cout << "\n\nLower bound of 6 is element "<< (lower - a.cbegin()) << " of array a";// 找到a中第一個大于6的元素auto upper = upper_bound(a.cbegin(), a.cend(), 6);cout << "\n\nUpper bound of 6 is element "<< (upper - a.cbegin()) << " of array a";auto eq = equal_range(a.cbegin(), a.cend(), 6);cout << "\n\nUsing equal_range:\n   Lower bound of 6 is element "<< (eq.first - a.cbegin()) << " of array a\n   "<< "Upper bound of 6 is element " << (eq.second - a.begin()) << " of array a";// lower_bound的使用場景cout << "\n\nUse lower_bound to locate the first point\n"<< "at which 5 can be inserted in order";lower = lower_bound(a.cbegin(), a.cend(), 5);cout << "\n   Lower bound of 5 is element "<< (lower - a.cbegin()) << " of array a";// upper_bound的使用場景cout << "\n\nUse upper_bound to locate the last point\n"<< "at which 7 can be inserted in order";upper = upper_bound(a.cbegin(), a.cend(), 7);cout << "\n   Upper bound of 7 is element "<< (upper - a.cbegin()) << " of array a";// equal_range的使用場景cout << "\n\nUse equal_range to locate the first and\n"<< "last point at which 5 can be inserted in order";eq = equal_range(a.cbegin(), a.cend(), 5);cout << "\n   Lower bound of 5 is element "<< (eq.first - a.cbegin()) << " of array a"<< "\n   Upper bound of 5 is element " << (eq.second - a.cbegin()) << " of array a"<< endl;
}

運行結果:

在這里插入圖片描述

12.堆排序

堆排序是一種排序算法,排序時一個數組中的元素被組織成一個特殊的數據結構——堆。關于堆排序的原理,請在“數據結構”課程中學習。

#include <iostream>
#include <iterator>
#include <algorithm>
#include <array>
#include <vector>
using namespace std;int main()
{const size_t SIZE = 10;array<int, SIZE> init { 3, 100, 52, 77, 22, 31, 1, 98, 13, 40 };array<int, SIZE> a(init);ostream_iterator<int> output(cout, " ");cout << "Array a before make_heap:\n";copy(a.cbegin(), a.cend(), output);// 以array對象a中的元素建立一個堆make_heap(a.begin(), a.end());cout << "\nArray a after make_heap:\n";copy(a.cbegin(), a.cend(), output);// 對堆中的元素進行堆排序sort_heap(a.begin(), a.end());cout << "\nArray a after sort_heap:\n";copy(a.cbegin(), a.cend(), output);cout << "\nArray init contains:\n";copy(init.cbegin(), init.cend(), output);	cout << endl;vector<int> v;// 將init中的元素放入v中for (size_t i = 0; i < SIZE; ++i){v.push_back(init[i]);push_heap(v.begin(), v.end());cout << "\nv after push_heap(init[" << i << "]): ";copy(v.cbegin(), v.cend(), output);}cout << endl;for (size_t i = 0; i < v.size(); ++i){cout << "\nv after " << v[0] << " popped from heap\n";pop_heap(v.begin(), v.end() - i);copy(v.cbegin(), v.cend(), output);}cout << endl;
}

運行結果:

在這里插入圖片描述

在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述

13.min、max、minmax和minmax_element

在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述

示例代碼:

#include <iostream>
#include <array>
#include <algorithm>
using namespace std;int main()
{cout << "The minimum of 12 and 7 is: " << min(12, 7);cout << "\nThe maximum of 12 and 7 is: " << max(12, 7);cout << "\nThe minimum of 'G' and 'Z' is: " << min('G', 'Z');cout << "\nThe maximum of 'G' and 'Z' is: " << max('G', 'Z');auto result1 = minmax(12, 7);cout << "\n\nThe minimum of 12 and 7 is: " << result1.first<< "\nThe maximum of 12 and 7 is: " << result1.second;array <int, 10> items { 3, 100, 52, 77, 22, 31, 1, 98, 13, 40 };ostream_iterator<int> output(cout, " ");cout << "\n\nArray itmes contains: ";copy(items.cbegin(), items.cend(), output);auto result2 = minmax_element(items.cbegin(), items.cend());cout << "\nThe minimum element in items is: " << *result2.first<< "\nThe maximum element in items is: " << *result2.second<< endl;
}

運行結果:

在這里插入圖片描述

三、函數對象

很多算法允許以一個函數指針來作為參數以完成它的任務。例如,上面的minmax_element算法就有一個重載的版本,需要用戶提供一個函數來完成元素的比較任務。

任何可以接收函數指針的算法都可以接收一個類的對象,只要這個類提供我們之前學習過的重載函數運算符operator(),且該重載的運算符符合算法的需要。例如在上面的minmax_element算法中,重載的函數運算符必須接收兩個參數,并返回一個bool值。這樣的類的對象就被稱為函數對象,它們在語法和語義上可以像函數或函數指針一樣使用。

函數對象較函數指針的優越之處

與函數指針相比,函數對象有若干優點。

編譯器可以內聯函數對象重載的operator()來提高性能(使用函數指針,每次調用都需要進行函數調用,會產生額外開銷)。

而且,由于是類的對象,函數對象可以具有該重載函數運算符用來執行任務的數據成員(函數指針可以使用全局變量或靜態變量來做到,但是會存在安全隱患)。

標準模板庫中預先定義的函數對象

我們在之前的算法中也曾使用過函數對象less<T>來指定容器元素排序的次序。

標準庫函數對象類型
divides<T>算術
equal_to<T>關系
greater<T>關系
greater_equal<T>關系
less<T>關系
less_equal<T>關系
logical_and<T>邏輯
logical_not<T>邏輯
logical_or<T>邏輯
minus<t>算術
modulus<T>算術
negate<T>算術
not_equal_to<T>關系
plus<T>算術
multiplies<T>算術

使用標準庫的accumulate算法

該算法我們在前面數學算法中也已經介紹過了,下面我們將創建一個程序,再次使用該算法,一次使用函數指針,一次使用函數對象。

#include <iostream>
#include <array>
#include <algorithm>
#include <numeric>
#include <functional>
#include <iterator>
using namespace std;int sumSquares(int total, int value)
{return total + value * value;
}template <class T>
class SumSquaresClass
{
public:T operator()(const T& total, const T& value){return total + value * value;}
};int main()
{const size_t SIZE = 10;array <int, SIZE> integers { 1,2,3,4,5,6,7,8,9,10 };ostream_iterator<int> output(cout, " ");cout << "array integers contains:\n ";copy(integers.cbegin(), integers.cend(), output);// 使用函數指針作為參數,計算序列中元素的平方和int result = accumulate(integers.cbegin(), integers.cend(), 0, sumSquares);cout << "\n\nSum of squares of elements in integers using "<< "binary\nfunction sumSquares: " << result;// 使用函數對象result = accumulate(integers.cbegin(), integers.cend(), 0, SumSquaresClass<int>());cout << "\n\nSum of squares of elements in integers using "<< "binary\nfunction sumSquares: " << result << endl;
}

運行結果:

在這里插入圖片描述

上面代碼中的SumSquaresClass<int>()其實就是,使用特化的類SumSquaresClass<int>調用該類的默認構造函數來實例化了一個函數對象,并將該函數對象作為accumulate算法的參數,之后該算法會使用該對象來調用它的成員函數operator(),從而實現函數調用的功能。

四、lambda表達式

在上面的內容中,我們能夠發現有許多的算法可以接收函數指針或函數對象這樣的參數。在將函數指針或函數對象傳遞給算法作參數之前,相應的函數或類必須已經聲明。

C++11的lambda表達式(或lambda函數)這一新特性,使程序員可以在將函數對象傳遞給一個函數的地方定義匿名的函數對象。lambda表達式被局部地定義在函數內,可以(以按值或按引用的方式)獲得它所在函數的局部變量,然后在lambda表達式的體中操作這些變量。

下面展示了一個簡單的lambda表達式的使用示例,它把一個int類型array對象的每個元素值加倍:

#include <iostream>
#include <array>
#include <algorithm>
using namespace std;int main()
{const size_t SIZE = 4;array<int, SIZE> values { 1,2,3,4 };// 為指定序列中的每個元素進行同樣的處理for_each(values.cbegin(), values.cend(),// 一個 lambda 表達式[](int i){ cout << i * 2 << endl; });int sum = 0;for_each(values.cbegin(), values.cend(),// 一個 lambda 表達式[&sum](int i) { sum += i; });cout << "Sum is " << sum << endl;
}

運行結果:

在這里插入圖片描述

lambda表達式lambda導引器([]),然后是形參表函數體。如果函數體是形如return 表達式;的單條語句,那么返回值類型可以自動地被推斷出來;否則,默認的返回值類型為void,或者可以顯式地使用尾隨返回值類型指明。例如,[](int i) -> int { return i * i; }。編譯器會將這個lambd表達式轉換成一個函數對象。

對于上面的示例代碼中第一個的lambda表達式接收一個int類型的參數ifor_each算法將array對象的每個元素傳遞給這個lambda表達式,由該lambda表達式輸出array中元素值的2倍。

第二個lambda表達式接收一個int類型的參數,且它的lambda導引器[&sum]表明該lambda表達式以按引用的方式獲得了當前程序中的局部變量sum,因此這個lambda表達式可以修改該局部變量的量。如果沒有&符號,則是按值的方式獲得局部變量,也就無法修改當前程序中的sum變量的值。

lambda表達式還可以賦值給變量,然后這個變量可以用來調用lambda表達式或者將它傳遞給其他函數。例如,auto myLambda = [](int i) { cout << i * 2 << endl; };
之后就可以像一個函數名一樣使用此變量名來調用該lambda表達式,如下所示:
myLambda(10); // 輸出20

五、標準庫算法總結

C++標準中將算法分為了幾類,包括改變序列的算法、不改變序列的算法、排序及相關算法、廣義的數值運算等。

該部分內容較多,請自行前往cppreference查閱。

對于標準模板庫中提供的算法,它們提供了實現一種功能的通用方法,所以算法只是將功能抽象出來,使得不同的容器只要能夠滿足最低的迭代器要求,那么就可以使用相同的算法來實現相同的功能,即使它們容器內部中要實現該功能的函數其實不一樣。

對于STL中的容器、算法和迭代器這三個主要組件,容器規定數據的存儲結構以及對這些數據的一些操作方法,算法定制了通用功能的實現,而算法通過迭代器來訪問容器中的數據,使得定制的功能能夠在對應容器中被執行。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/84183.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/84183.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/84183.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Vue 項目實戰:三種方式實現列表→詳情頁表單數據保留與恢復

背景&#xff1a;在Vue項目中&#xff0c;實現列表頁跳轉詳情頁并保留表單數據&#xff0c;返回時恢復表單狀態。 核心功能&#xff1a; 保存緩存&#xff1a;點擊查詢按鈕時&#xff0c;表單數據保存恢復緩存&#xff1a;從詳情頁返回時&#xff0c;恢復表單數據清除緩存&…

iptables實驗

實驗一&#xff1a;搭建web服務&#xff0c;設置任何人能夠通過80端口訪問。 1.下載并啟用httpd服務器 dnf -y install httpd 開啟httpd服務器 systemctl start httpd 查看是否啟用 下載并啟用iptables&#xff0c;并關閉firewalld yum install iptable…

Razor編程RenderXXX相關方法大全

文章目錄 第一章&#xff1a;RenderXXX方法概述1.1 RenderXXX方法的作用與意義1.2 基本工作原理1.3 主要方法分類 第二章&#xff1a;部分視圖渲染方法2.1 Html.RenderPartial()2.2 Html.RenderAction()2.3 性能對比分析 第三章&#xff1a;視圖組件渲染方法3.1 Html.RenderCom…

Go 語言 range 關鍵字全面解析

Go 語言 range 關鍵字全面解析 range 是 Go 語言中用于迭代數據結構的關鍵字&#xff0c;支持多種數據類型的遍歷操作。它提供了一種簡潔、安全且高效的方式來處理集合類型的數據。 基本語法 for index, value : range collection {// 循環體 } 1. 數組/切片迭代 fruits :…

美化顯示LLDB調試的數據結構

前面的博文美化顯示GDB調試的數據結構介紹了如何美化顯示GDB中調試的數據結構&#xff0c;本文將還是以mupdf庫為例介紹如何美化顯示LLDB中調試的數據結構。 先看一下美化后的效果&#xff1a; 一、加載自定義腳本 與GDB類似&#xff0c;需要添加一個~/.lldbinit文件&#xf…

【Java學習筆記】日期類

日期類 第一代日期類&#xff1a;Date 引入包 import java.text.ParseException&#xff1a;日期轉換可能會拋出轉換異常 import java.text.SimpleDateFormat import java.util.Date 1. 基本介紹 Date&#xff1a;精確到毫秒&#xff0c;代表特定的瞬間 SimpleDateForma…

C++基礎進階:函數、內聯函數與Lambda函數詳解

引言 在C編程的旅程中&#xff0c;函數是構建復雜程序的基本單元。它們像樂高積木一樣&#xff0c;允許我們將代碼分解成更小、更易于管理的部分。今天&#xff0c;我們將深入探討C中的三種重要函數類型&#xff1a;普通函數、內聯函數以及Lambda函數。掌握它們&#xff0c;將…

從Node.js到React/Vue3:流式輸出技術的全棧實現指南

本文將從底層原理到工程實踐&#xff0c;完整解析如何使用Node.js后端結合React和Vue3前端實現流式輸出功能&#xff0c;涵蓋協議選擇、性能優化、錯誤處理等關鍵細節&#xff0c;并通過真實場景案例演示完整開發流程。 一、流式輸出的核心原理與協議選擇 1.1 流式傳輸的底層機…

AT2401C中科微2.4g芯片PA

作為無線通信系統的核心模塊&#xff0c;射頻前端芯片通過整合功率放大器&#xff08;PA&#xff09;、濾波器、開關和低噪聲放大器&#xff08;LNA&#xff09;等關鍵組件&#xff0c;成為保障通信質量、降低功耗及維持信號穩定的決定性因素。 AT2401C是一款面向2.4GHz無線通信…

Linux安裝jdk、tomcat

1、安裝jdk sudo yum install -y java-1.8.0-openjdk-devel碰到的問題&#xff1a;/var/run/yum.pid 已被鎖定 Another app is currently holding the yum lock&#xff1b; waiting for it to exit… https://blog.csdn.net/u013669912/article/details/131259156 參考&#…

在本地電腦中部署阿里 Qwen3 大模型及連接到 Elasticsearch

在今天的文章中&#xff0c;我將參考文章 “使用 Elastic 和 LM Studio 的 Herding Llama 3.1” 來部署 Qwen3 大模型。據測評&#xff0c;這是一個非常不錯的大模型。我們今天嘗試使用 LM Studio 來對它進行部署&#xff0c;并詳細描述如何結合 Elasticsearch 來對它進行使用。…

【設計模式】2.策略模式

every blog every motto: You can do more than you think. https://blog.csdn.net/weixin_39190382?typeblog 0. 前言 商場收銀軟件為例 1. 基礎版 total 0def click_ok(price,num):tot price * numtotal totprint(合計&#xff1a;, total)增加打折 total 0def cli…

c++中的輸入輸出流(標準IO,文件IO,字符串IO)

目錄 &#xff08;1&#xff09;I/O概述 I/O分類 不同I/O的繼承關系 不同I/O對應的頭文件 &#xff08;2&#xff09;iostream 標準I/O流 iostream頭文件中的IO流對象 iostream頭文件中重載了<<和>> 緩沖區示意圖 標準輸入流 cin用法 cin&#xff1a;按空…

人工智能學習06-循環

人工智能學習概述—快手視頻 人工智能學習06-循環—快手視頻

【電路】阻抗匹配

&#x1f4dd; 阻抗匹配 一、什么是阻抗匹配&#xff1f; 阻抗匹配&#xff08;Impedance Matching&#xff09;是指在電子系統中&#xff0c;為了實現最大功率傳輸或最小信號反射&#xff0c;使信號源、傳輸線與負載之間的阻抗達到一種“匹配”狀態的技術。 研究對象&#x…

【vue】Uniapp 打包Android 文件選擇上傳問題詳解~

需求 uniapp兼容android app&#xff0c;pc&#xff0c;h5的文件選擇并上傳功能。 需要支持拍照和相冊選擇&#xff0c;以及選擇其他類型文件上傳~ 實踐過程和問題 開始使用uni-file-picker組件 以為很順利&#xff0c;android模擬器測試…… 忽略了平臺兼容性提示~&#…

Python:操作 Excel 格式化

??Python 操作 Excel 格式化完整指南(openpyxl 與 xlsxwriter 雙方案) 在數據處理和報表自動化中,Python 是一把利器,尤其是配合 Excel 文件的讀寫與格式化處理。本篇將詳細介紹兩大主流庫: openpyxl:適合讀取與修改現有 Excel 文件xlsxwriter:適合創建新文件并進行復…

Prompt Enginering(提示工程)先進技術

前沿 CoT&#xff08;Chain-of-Thought&#xff09;和 ReACT&#xff08;Reasoning and Acting&#xff09;是兩種先進的 Prompt Engineering&#xff08;提示工程&#xff09; 技術&#xff0c;旨在提升大語言模型&#xff08;LLM&#xff09;的推理、規劃和執行能力。 CoT&a…

【C++系列】模板類型特例化

1. C模板類型特例化介紹 ??定義??&#xff1a;模板類型特例化&#xff08;Template Specialization&#xff09;是C中為模板的特定類型提供定制實現的機制&#xff0c;允許開發者對通用模板無法處理的特殊類型進行優化或特殊處理。 ??產生標準??&#xff1a; C98/03…

AI數據分析在體育中的應用:技術與實踐

在現代體育競技領域&#xff0c;"數據驅動"已不再是一個遙遠的概念。尤其隨著人工智能&#xff08;AI&#xff09;和大數據分析的不斷成熟&#xff0c;從職業俱樂部到賽事直播平臺&#xff0c;從運動員訓練到球迷觀賽體驗&#xff0c;AI正以前所未有的方式滲透并改變…