cpp_标准库算法demo

// Copyright (c) 2020 by cpp 实战笔记
//
// g++ algo.cpp -std=c++11 -o a.out;./a.out
// g++ algo.cpp -std=c++14 -o a.out;./a.out
// g++ algo.cpp -std=c++14 -I../common -o a.out;./a.out

#include 

#include 

#include 
#include 
#include 
#include 

using namespace std;

// C++ 里的算法,指的是工作在容器上的一些泛型函数
void case1()
{
    // vector 容器 定义及初始化
    vector v = {1,3,1,7,5}; 

    // count 计算容器中元素的数量,begin() 和end() 指定容器范围 ,每个元素计数1 
    auto n1 = std::count(
        begin(v), end(v), 1
    );

    // 等价于手写循环实现统计。
    int n2 = 0;
    for(auto x : v) {
        if (x == 1) {
            n2++;
        }
    }

    assert(n1 == n2);

    // count_if 计算满足一定条件的元素的数量,
    // begin(), end() 指定 容器范围,第三个参数可以传递lambda 函数 定义 条件函数
    auto n = std::count_if(
        begin(v), end(v),
        [](auto x) {
            return x > 2;
        }
    );

    assert(n == 3);
}


// 算法操作容器,但实际上它看到的并不是容器,而是指向起始位置和结束位置的迭代器,
// 算法只能通过迭代器去“间接”访问容器以及元素,算法的能力是由迭代器决定的。
// 这是泛型编程的理念,与面向对象正好相反,分离了数据和操作。算法可以不关心容器的内部结构,
// 以一致的方式去操作元素,适用范围更广,用起来也更灵活。
void case2()
{
    vector v = {1,2,3,4,5};

    auto iter1 = v.begin();  // 成员函数的迭代器 开头
    auto iter2 = v.end();    // 成员函数的迭代器 结尾

    auto iter3 = std::begin(v);  // 全局函数获取迭代器 获取开头,自动类型推导
    auto iter4 = std::end(v);   // // 全局函数获取迭代器 获取结尾,自动类型推导

    // 建议使用更加通用的全局函数 begin()、end(),虽然效果是一样的,但写起来比较方便,看起来也更清楚
    //(另外还有 cbegin()、cend() 函数,返回的是常量迭代器)
    assert(iter1 == iter3);
    assert(iter2 == iter4);
}

// 迭代器计算相关的函数
void case3()
{
    array arr = {0,1,2,3,4};  // array静态数组容器

    auto b = begin(arr); // 全局函数获取迭代器,首端
    auto e = end(arr); // 全局函数获取迭代器,末端

    assert(distance(b, e) == 5);  // distance 计算迭代器的距离

    auto p = next(b);  // next 获取迭代器"下一个" 位置
    assert(distance(b, p) == 1);  // 迭代器距离
    assert(distance(p, b) == -1);  // distance 可以反向计算迭代器距离。

    advance(p, 2);  // advance 使迭代器前进 2 个位置。
    assert(*p == 3);
    assert(p == prev(e, 2));  // prev 计算迭代器 的前 2 个位置。

}


// 常用 的函数式 编程相关的函数
void case4()
{
#if 1
    vector v = {3,5,1,7,10};

    // range for 循环方式
    for(const auto& x : v) {
        cout << x << ",";
    }
#endif
    cout << endl;

#if 1
    // 定义一个打印功能的lambda 函数
    auto print = [](const auto& x)
    {
        cout << x << ",";
    };

// for_each 算法的价值就体现在,把要做的事情分成了两部分,也就是两个函数:一
// 个遍历容器元素,另一个操纵容器元素,而且名字的含义更明确,代码也有更好的封装。
// 建议你尽量多用 for_each 来替代 for,因为它能 够促使我们更多地以“函数式编程”来思考,
// 使用 lambda 来封装逻辑,得到更干净、更安全的代码。
    // for_each 算法 对容器内的每个函数执行同样的操作
    for_each(cbegin(v), cend(v), print);
#endif
    cout << endl;

#if 1
    // for_each 可以直接传递 lambda 函数的方式
    for_each(
        cbegin(v), cend(v),
        [](const auto& x)
        {
            cout << x << ",";
        }
    );
#endif
    cout << endl;
}

void case5()
{
    vector v = {3,5,1,7,10,99,42};

    auto print = [](const auto& x)
    {
        cout << x << ",";
    };

    // total sort 排序算法,全排序
    std::sort(begin(v), end(v));  // 默认快拍
    for_each(cbegin(v), cend(v), print);
    cout << endl;

    // top3 排序, partial_sort 可以完成top n 排序
    std::partial_sort(
        begin(v), next(begin(v), 3), end(v));
    for_each(cbegin(v), cend(v), print);
    cout << endl;

    // best3 nth_element 选出 最好的 n 个元素,但不排序
    std::nth_element(
        begin(v), next(begin(v), 3), end(v));
    for_each(cbegin(v), cend(v), print);
    cout << endl;

    // Median nth_element 可以求中位数
    auto mid_iter =
        next(begin(v), v.size()/2);
    std::nth_element( begin(v), mid_iter, end(v));
    for_each(cbegin(v), cend(v), print);
    cout << "median is " << *mid_iter << endl;

    // partition  找出所有大于9 的元素
    auto pos = std::partition(
        begin(v), end(v),
        [](const auto& x)
        {
            return x > 9;
        }
    );
    for_each(begin(v), pos, print);
    cout << endl;
    for_each(cbegin(v), cend(v), print);
    cout << endl;

    // min/max  minmax_element 求出最大最小元素
    auto value = std::minmax_element(
        cbegin(v), cend(v)
    );
    cout << *value.first << ","
         << *value.second << endl;

// 以上排序算法最好在顺序容器 array/vector 上调用
}

void case6()
{
    vector v = {3,5,1,7,10,99,42};

    auto print = [](const auto& x)
    {
        cout << x << ",";
    };

    // total sort 先排序
    std::sort(begin(v), end(v));
    for_each(cbegin(v), cend(v), print);
    cout << endl;

    // 执行二分查找,确定元素是否存在
    auto found = binary_search(
        cbegin(v), cend(v), 7
    );
    cout << "found: " << found << endl;

    decltype(cend(v)) pos;  // 使用decltype,声明一个迭代器,

    pos = std::lower_bound(
        cbegin(v), cend(v), 7   // lower_bound, 找到第一个大于等于7 的元素的位置。
    );
    //assert(pos != cend(v));
    //assert(*pos == 7);
    found = (pos != cend(v)) && (*pos == 7);   // 可能找不到需要做判断
    assert(found);   // 7 在容器里

    pos = std::lower_bound(
        cbegin(v), cend(v), 9  // lower_bound 找到第一个大于等于 9 的元素的位置。
    );
    //assert(pos != cend(v));
    //cout << *pos << endl;
    found = (pos != cend(v)) && (*pos == 9);
    assert(!found);

    // lower_bound 的返回值是一个迭代器,所以就要做一点判断工作,才能知道是否真的找到了。
    // 判断的条件有两个,一个是迭代器是否有效,另一个是迭代器的值是不是要找的值。


    //  lower_bound 的查找条件是“大于等于”,而不是“等于”,所以它的真正含义是“大于等于值的第一个位置”。
    // 相应的也就有“大于等于值的最后一个位置”,算法叫upper_bound,返回的是第一个“大于”值的元素
    pos = std::upper_bound(
        cbegin(v), cend(v), 9  // 找到第一个大于9 的位置。
    );
    //  返回的形式 begin < x <= lower_bound < upper_bound < end
    //cout << *pos << endl;
    
    // equal_range() 可以找出有序序列中所有和给定元素相等的元素。它的前两个参数是指定序列的两个正向迭代器,第三个参数是要查找的元素
    // 算法会返回一个 pair 对象,first 指向的是不小于第三个参数的一个元素,second 指向大于第三个参数的一个元素
    auto range = std::equal_range(
        cbegin(v), cend(v), 9
    );
    //cout << *range.first << endl;
    //cout << *range.second << endl;
    for_each(
        range.first, std::next(range.second), print
    );
    cout << endl;
}

void case7()
{
    // 有序容器set、map 有等价的成员函数find/lower_bound/upper_bound,效果是一样的
    multiset s = {3,5,1,7,7,7,10,99,42};  // multiset 允许重复

    auto print = [](const auto& x)
    {
        cout << x << ",";
    };

    auto pos = s.find(7);
    assert(pos != s.end());   // 二分查找返回迭代器,需要和end 比较才能确定是否找到。

    auto lower_pos = s.lower_bound(7); // 获取区间的左端点
    auto upper_pos = s.upper_bound(7);  // 获取区间的右端点。

    for_each(
        lower_pos, upper_pos, print
    );
    cout << endl;
}

void case8()
{
    vector v = {1,9,11,3,5,7};

    decltype(v.end()) pos;  //  声明一个迭代器,使用decltype

    pos = std::find(
        begin(v), end(v), 3   // find 查找算法,找到第一个出现的位置
    );
    assert(pos != end(v));   // 与end()比较才能知道是否找到

    // find_if 查找算法,用lambda判断条件
    pos = std::find_if(   
        begin(v), end(v),
        [](auto x) {
            return x % 2 == 0;
        }
    );
    assert(pos == end(v));

    array arr = {3,5};
    // find_first_of 查找一个子区间
    pos = std::find_first_of(
        begin(v), end(v),
        begin(arr), end(arr)
    );
    assert(pos != end(v));
}

int main()
{
    case1();
    case2();
    case3();
    case4();
    case5();
    case6();
    case7();
    case8();

    using namespace std;

    cout << "algorithm demo" << endl;
}

你可能感兴趣的:(cpp_标准库算法demo)