简述泛型算法之 一认识算法

简述泛型算法之 一认识算法

本章都是些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表达式

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值