簡述泛型算法之 一認識算法


簡述泛型算法之 一認識算法

本章都是些STL算法的使用,意思明確,代碼簡介,所以直接寫代碼,實踐一下各種算法。

初識算法

算法永遠不會改變底層容器大小,智可能改變容器中保存的元素,也可能移動元素,但不會直接添加或刪除元素;
除了insert iterator,重載了賦值運算符。但算法自身永遠不會做這樣的操作;

//find count
vector<int> v1{1, 2, 3, 4, 3, 3};
auto iter = find(v1.begin(), v1.end(), 6);
if (iter != v1.end()) {
    cout << *iter << endl;
} else {
    cout << "not found" << endl;
}

auto iter = count(v.begin(), v.end(), 3);
cout << iter << endl;

只讀算法

只讀算法:只會讀取其輸入范圍中的元素,不會改變元素
對於只讀算法,最好調用cbegin()和cend();但如果需要用算法返回的迭代器來改變元素,就需要用begin()和end()

vector<int> v{1, 2, 3, 4, 3, 3};
vector<int> v1{1, 2, 3, 4, 3, 3};
vector<int> v2{1, 2, 3, 4, 5, 6};
vector<string> vsa{"tx", "bd", "al", "ms", "ne"};
vector<string> vsb{"tx", "xm", "bd"};

//accumulate
//算法的第三個數據類型決定了調用那個加法運算符以及返回類型
//序列中的元素必須與第三個參數匹配,或者能轉換為第三個類型的參數
{
    int sum = accumulate(v.begin(), v.end(), 0);

    //錯誤:const char*沒有+運算符
    // string ssum = accumulate(vs.begin(), vs.end(), "");

    string ssum;
    ssum = accumulate(vsa.cbegin(), vsa.cend(), ssum);
    cout << ssum << endl;
}

//equal
//1 equal利用迭代器完成操作,因此,可以利用不同類型的容器中的元素,只要保證能用==來比較兩個元素類型即可
//2 基於一個重要的假設:第二個序列至少與第一個序列一樣長;因此,如果想判斷兩個序列完全相等,必須先判斷size
//3 對於只接受單一迭代器表示第二個序列的算法,都假定第二個序列至少與第一個序列一樣長
{
    vector<string> vs1{"tx", "xm", "bd"};
    vector<const char*> vs2{"tx", "xm", "bd", "al"};

    cout << equal(vs1.cbegin(), vs1.cend(), vs2.cbegin()) << endl;

    if (vs1.size() == vs2.size() &&
        equal(vs1.cbegin(), vs1.cend(), vs2.cbegin())) {
        cout << "equal range";
    } else {
        cout << "non equal";
    }
}

小問題:如果兩個vector中保存的都是const char* 不是string, equal會發生什么?
解答:string類重載了==,可比較兩個字符串長度是否向指向其中元素對應位是否相等。
而const char* 本質上是指針類型,用==比較指針對象,僅僅是比較的是地址是否相等,而不是比較其中的字符是否相同;因此,失去了使用equal的邏輯意義。

寫容器算法

一些算法會將新值賦予序列中的元素。當使用這些算法時,確保原序列大小至少不小於要求算法寫入的元素數目;
不論如何,算法本身不會執行容器操作,算法自身不可能改變容器大小。(除了inserter)

用到的自定義函數:

template <typename T>
inline void InsertElement(T& v, int first, int last) {
    for (int i = first; i <= last; ++i) {
        v.insert(v.end(), i);
    }
}

template <typename T>
inline void PrintElement(const T& v, const string& s = "") {
    if ("" != s) {
        cout << s << ": ";
    }
    for (const auto & elem : v) {
        cout << elem << " ";
    }
    cout << endl;
}

具體算法使用如下:

//fill 將給定序列賦予給定的值
{
    vector<int> v1;
    InsertElement(v1, 1, 8);
    PrintElement(v1);

    fill(v1.begin(), v1.end(), 99);
    PrintElement(v1);
}

//fill_n 將給定值賦給迭代器指向的元素開始的指定個元素
//該算法假定寫入n個元素是安全的;
{
    vector<int> v;
    InsertElement(v, 1, 8);

    fill_n(v.begin(), v.size(), 1);
    PrintElement(v);

    //初學容易犯的錯誤是:在空容器上調用fill_n
    vector<int> v2;
// fill_n(v2.begin(), 10, 0); //災難

    //合法。因為back_inserter會調用容器的push_back,即此時不再是賦值,而是插入
    fill_n(back_inserter(v2), 10, 12);
    PrintElement(v2);
}
//copy
{
    //copy:傳遞給copy的目的序列至少要包含與輸入序列一樣多的元素
    vector<int> v(10, 0);
    list<int> l(10, 2);
    PrintElement(l);

    copy(v.begin(), v.end(), l.begin());
    PrintElement(l);

    list<int> ll;
    copy(v.begin(), v.end(), back_inserter(ll));
    PrintElement(v, "copy back_inserter");
}
//replace
//replace_copy
{
    //replace(v.begin(), v.end(), a, b); 將輸入序列中a替換為b
    vector<int> src{3, 3, 1, 1, 1, 1, 1, 4};
    PrintElement(src);

    //replace
    replace(src.begin(), src.end(), 3, 9);
    PrintElement(src);  //直接修改源序列

    //replace_copy
    vector<int> dest2(src.size());
    replace_copy(src.begin(), src.end(), dest2.begin(), 1, 3);
    PrintElement(dest2);

    //replace_copy back_inserter
    vector<int> dest;
    replace_copy(src.begin(), src.end(), back_inserter(dest), 1, 0);
    PrintElement(src);  //不直接修改源序列修改
    PrintElement(dest); //把修改后的序列插入到dest中
}

//reverse
//reverse_copy
{
    vector<int> v;
    InsertElement(v, 1, 9);
    PrintElement(v, "before reverse");

    reverse(v.begin(), v.end());
    PrintElement(v, "afer reverse");

    vector<int> vi;
    reverse_copy(v.begin(), v.end(), back_inserter(vi));
    PrintElement(v, "reverse source");
    PrintElement(vi, "reverse_copy");
}

//unique
//unique將相鄰的重復項“消除”, 返回一個指向不重復值范圍的末尾迭代器;
//“消除”指的是用覆蓋相鄰的重復元素
{
    vector<string> vs{"the", "quick", "red", "fox", "jumps", "over", "the","slow", "red", "turtle"};
    PrintElement(vs, "before sort");

    sort(vs.begin(), vs.end());
    PrintElement(vs, "after sort");

    auto end_unique = unique(vs.begin(), vs.end()); //此時並沒有消除重復的元素
    vs.erase(end_unique, vs.end());
    PrintElement(vs, "after unique");
}

定制操作

向算法傳遞函數:
sort函數默認使用元素類型的<運算符排序,但有時還希望按照長度排序;長度相同時按再按照字典序排序,為了能按照長度排序,將使用sort的第二個版本。
此版本是重載過的,接受第三個參數,此參數是一個謂詞(predicate):一個可調用表達式,返回結果是一個可以用作條件的值

ex1:按字典序重排word,消除重復元素;按長度重排,長度相同維持字典序

//自定義操作
bool isShorter(const string& s1, const string& s2) {
    return s1.size() < s2.size();
}

vector<string> vs{"the", "quick", "red", "fox", "jumps", "over", "the","slow", "red", "turtle"};

sort(words.begin(), words.end());
auto end_unique = unique(words.begin(), words.end());
words.erase(end_unique, words.end());

stable_sort(vs.begin(), vs.end(), isShorter);    //向算法傳遞函數參數

ex2:打印出長度大於等於5的元素

//自定義操作
bool longer_than_5(const string& s) {
    return s.size() >= 5;
}

auto end_partition = partition(vss.begin(), vss.end(), longer_than_5);
for (auto iter = vss.begin(); iter != end_partition; ++iter) {
    cout << *iter << " ";
}

lambda表達式的必要

在上個例子中,如果要求算法分割的長度大於等於n,此時,自定義函數無能為力,因為算法只接受一個函數參數。而長度n應該是程序中的變量,要想讓算法能夠使用該變量,需要使用lambda表達式。
詳情見下篇文檔簡述泛型算法之 二lambda表達式


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com