C++容器演算法

LemHou發表於2024-08-27

容器演算法

<algorithm>c++自帶的容器演算法,提供一系列實用的演算法。在談到容器演算法,我們大機率會用到謂詞predicate,謂詞返回的型別是布林型別(bool)可以是lambda表示式、函式物件以及其它可呼叫的物件。

查詢

  1. find()查詢元素

    find接受三個引數,第三個引數是值型別,setmap自帶count函式也能實現這樣的功能,返回值0表示不存在。為了方便本次連帶find_iffind_if_notfind_first_offind_endadjacent_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;
    

去重

  1. unique(nums.begin(), nums.end())除掉連續相同的值

    unique函式的作用是刪除掉的連續的相同的值,unique還支援傳入二元謂詞(可以理解為一個引數的函式),會返回一個迭代器it,但是並不是end(),中間是未指定的值(訪問可能會產生未定義行為)。注意不要用setmap,返回的都是常量迭代器是無法改變的,即其指向的元素無法改變。

    // 無謂詞寫法
    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
    
  2. 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,才會進行復制
    

排序

  1. sort()對容器進行排序

    sort用於實現容器元素的排序,sort也同樣接受二元謂詞

    sort(a.begin(), a.end());
    sort(a.begin(), a.end(), greater<>()); // 使用庫中自帶的greater\less物件
    

迭代器差值

  1. distance()求迭代之間距離

    distance用於求算兩個迭代器之間的差值,只用於同一容器, setmapvector都可以用。

    distance(c.begin(), c.end());
    

遍歷容器

  1. 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個元素進行操作。
    

複製元素

  1. 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個
    
  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在輸出到結果容器中只會輸出第一個容器中的元素(不管第一個容器的元素是否少於第二個容器的元素)。這四個函式都接受一個二元謂詞。注意:mapset都無法使用,因為這三個函式底層實現都需要用到自增運算子,而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接受一個二元謂詞,兩個函式都能夠使用setmapsearch接受一個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}

相關文章