第十章、泛型算法

本文详细介绍了C++标准库中的多种算法,包括只读算法、写容器算法及排序算法等。重点讲解了find、accumulate、sort等常用算法的使用方法及细节,并对比了stable_sort与sort的不同之处。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

大多数算法定义在头文件algorithm中, 在头文件numeric中定义了一组数值泛型算法(如equal)。


只读算法
  • find

    • find(vec.begin(),vec.end(),val);

    • 查找val的值,返回指向 第一个值等于val的 元素 的迭代器。

  • accumulate (定义在numeric)

    • accumulate(vec.begin(),vec.end(),0);
    • 初始值为0,将vec中各元素相加。
    • 第三个参数类型决定了使用哪个加法运算符 以及返回值的类型
    • eg: string sum=accumulate(v.cbegin(),c.cend(),string(""));
    • ↑ string串相加。

字符串字面值 “ ”,的类型是 const char* ,是一个常量,所以不能进行加减操作。

  • equal
    • equal(roster1.cbegin(),roster1.cend(),roster2.cbegin())
    • 依次比较两个序列的值。
    • 因为第二个序列的迭代器只接收了首元素,所以要求第二个序列至少要和第一个序列一样长。

写容器的算法
  • fill
    • fill(vec.begin(),vec.end(),0);
    • 将0写入vec中每个元素。
    • 只要传输的是一个有效的范围,写操作就是安全的。
    • fill_n(iter,n,val);// 向iter后的n个元素写入val
      • 会出现越界问题。
      • 为了解决越界问题 采取的方法是 使用插入迭代器
  • copy
    • copy(vec.begin(),vec.end(),mubiao.begin());
    • replace(vec.begin(),vec.end(),目标元素,替换的元素);
    • 会改变原序列
    • replace_copy(vec.begin(),vec.end(),插入迭代器,目标元素,替换的元素);
    • 之后通过插入迭代器生成了新的序列,原序列不变。

排序算法
  • sort (按首字母字典序排列)
    • sort(vec.begin(),vec.end());
    • sort(vec.begin(),vec.end(),[](const string& str_1,const string& str_2){return str_1.size()>str_2.size()})
    • 重载形式可以接受一个二元谓词
  • unique
    • auto end_unique=unique(vec.begin(),vec,end());
    • 去重(将重复元素移动到末尾)。并返回不重复范围末尾的迭代器。
    • unique就是让连续的相同值变成一个 (所以一般要配合sort使用)
  • stable_sort (按长度排序,并且相同长度维持字典序)
    • 使相同长度的元素按字典序排列。
    • 接受一个二元谓词。
stable_sort和sort的区别
  • stable_sort的用法与sort一致,区别是stable_sort函数遇到两个数相等时,不对其交换顺序;这个应用在数组里面不受影响,当函数参数传入的是结构体时,会发现两者之间的明显区别

查找
  • find_if
    • auto wc=find_if(vec.begin(),vec.end(),isPrimer);
    • 查找序列中第一个满足isPrimer的对象(isPrimer是一个可调用对象)。并返回一个指向他的迭代器。
    • 接受一个一元谓词。
  • for_each
    • for_each(vec.begin(),vec.end(),func);
    • func是一个可调用对象,for_each对序列中所有对象执行func。
    • 接受一个一元谓词。

lambda (C++11)
  • aufo f=[capture list](parameter list) ->int {return 1;};

  • lambda是一个可调用对象 f(args); args是一个以逗号为分割的参数列表。

  • 捕获方式

    • 值捕获

    • 就是普通的捕获…可以捕获多个值

    • 在定义时捕获
    • 如果想通过捕获的变量改变原来的变量…

      • auto f=[vi]()mutable{return ++vi;};
    • 引用捕获

    • 对于无法进行拷贝的对象,如 ostream,

      • for_each(words.begin(),words.end(),[&os,c](const string &s){os<<s<<c;});
    • 当然也可以对于普通的变量进行引用捕获,但影响是只有在调用lambda语句时才进行捕获,如果在之前改变了所引对象的值,捕获的值也会变化。

    • 隐式捕获

    • = 值捕获, & 引用捕获。

    • [&,c] means: 变量c使用值捕获,其他使用引用捕获
    • [=, &c] : 变量c使用引用捕获,其他使用值捕获。
  • 返回类型

    • lambda只能单一返回。 如果出现两个return 则会默认返回类型是void。 所以在这种情况下要人为规定返回类型。

    • 在parameter list之后, function body之前使用, -> int


参数绑定
  • 使用bind语句,可以实现类似lambda的功能,也可以用于更改形参顺序等。
    • 对于lambda 如果要在多处使用,需要多次定义。所以如果要多处使用相同的lambda操作,需要一个函数来代替。
    • …….以后再看 太繁琐了,,

插入迭代器
  • back_inserter 接收一个参数。为要插入的对象
  • front_inserter 接受一个参数。同上
  • inserter 接收两个参数,一个是要插入的对象,另一个是指向给定容器位置的迭代器, 元素会被插入到迭代器所指位置之前。

流迭代器
  • istream_iterator
  • ostream_iterator

反向迭代器
  • reverse_iterator
    • 使用vec.rbegin(), vec.rend() 作为迭代器,分别指向尾元素(最后一个元素之后)的前一个元素首元素的前一个元素(什么都没有)
    • 反向迭代器: 右闭合区间 (vec.crend(),vec.crbegin()]
      • 是按元素顺序写的,实际使用反向迭代器时,还是crbegin在前,crend在后。(不然也不能反向了233)。
    • 正向迭代器: 左闭合区间 [vec.begin(),vec.end() )
    • 有一个成员函数 base(), 可以把向左移动的迭代器转为向右移动的迭代器, 向右时,迭代器指向的元素为原来向左时指向元素的右边那个。

泛型算法结构(分类方式)
  • 根据算法本身功能分类
    • 根据只读 ,只写 ,重新排序 分类。
  • 根据可以使用的迭代器操作分类。
    • 输入迭代器
    • 只读不写,单遍扫描,只能递增。支持判定运算符(== , !=) 、解引用(*)(只能出现在赋值运算右侧)、箭头运算符(->)
    • 输出迭代器
    • 只写不读,单遍扫描,只能递增,支持解引用(*)()只能出现在赋值号左侧。
    • 前向迭代器
    • 支持上述所有操作+多遍扫描,只能递增。
    • 双向迭代器 (如list的迭代器)
    • 支持上述所有操作+多遍扫描+可递增也可递减
    • 随机访问迭代器 (vector的迭代器)
    • 支持所有操作+比较两个迭代器相对位置的关系运算符(> , < , <=, >=)+ 迭代器和一个整数值的加减运算(+, +=, - ,-=) + 两个迭代器的减法运算符 (-)。

算法命名规则
  • _if

  • _copy

特定容器算法
  • 链表的splice算法

  • 
    #include <iostream>
    
    
    #include <list>
    
    
    #include <string>
    
    
    #include <algorithm>
    
    using namespace std;
    int main ()
    {
    list<int> mylist1, mylist2;
    list<int>::iterator it;
    // set some initial values:
    for (int i=1; i<=4; i++)
       mylist1.push_back(i);      // mylist1: 1 2 3 4
    for (int i=1; i<=3; i++)
       mylist2.push_back(i*10);   // mylist2: 10 20 30
    it = mylist1.begin();
    ++it;                         // points to 2
    mylist1.splice (it, mylist2); // mylist1: 1 10 20 30 2 3 4
                                  // mylist2 (empty)
                                  // "it" still points to 2 (the 5th element)                                     
    mylist2.splice (mylist2.begin(),mylist1, it);
                                  // mylist1: 1 10 20 30 3 4
                                  // mylist2: 2
                                  // "it" is now invalid.
    it = mylist1.begin();
    advance(it,3);                // "it" points now to 30
    
    mylist1.splice ( mylist1.begin(), mylist1, it, mylist1.end());
                                  // mylist1: 30 3 4 1 10 20
    
    cout << "mylist1 contains:";
    for (it=mylist1.begin(); it!=mylist1.end(); it++)
      cout << " " << *it;
    
    cout << "/nmylist2 contains:";
    for (it=mylist2.begin(); it!=mylist2.end(); it++)
      cout << " " << *it;
    cout << endl;
    
    list<string> dictionary, bword;
    dictionary.push_back("any");
    dictionary.push_back("angle");
    dictionary.push_back("ajust");
    dictionary.push_back("common");
    dictionary.push_back("cannon");
    dictionary.push_back("company");
    bword.push_back("blue");
    bword.push_back("banana");
    bword.push_back("break");
    list<string>::iterator its = dictionary.begin();
    for (int i = 0; i < 3; i++)
        its++;
    dictionary.splice(its, bword);
    copy(bword.begin(), bword.end(), ostream_iterator<string>(cout, "/n"));
    return 0;
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值