标准库定义。
算法:因为它们实现了一些经典算法的公共接口,如排序和搜索。
泛型:因为可以用于不同类型的元素和多种容器类型(不仅包括标准库类型,如 vector 或 list, 还包括内置的数组类型等)。
标准库定义了大约100个类型无关的对序列进行操作的算法。序列可以是标准库容器类型中的元素、 一个内置数组或者是(例如)通过读写 一个流来生成的。算法通过在迭代器上进行操作来实现类型无关。
大部分在algorithm头文件中,numeric头文件中定义了一组数值泛型算法。
算法分类
只读算法
除了少数例外,标准库算法都对一个范围内的元素进行操作。我们将此元素范围称为“输入范围”。接受输入范围的算法总是使用前两个参数来表示此范围,两个参数分别是指向要处理的第一个元素和尾元素之后位置的迭代器。
只读算法,只会读取其输入范围内的元素,而从不改变元素如find、accumulate、equal算法。
int ia[] = {27, 210, 12, 47, 109, 83) ;
int val = 83;
int* result = find(begin(ia), end(ia), val);
int sum = accumulate(vec.cbegin(), vec.cend(), O);
//参数形如下面这种格式的,都假定的c2的输入范围 >= c1的输入范围。
equal(c1.cbegin(), c1.cend(), c2.cbegin());
写算法
将新值赋予序列中的元素。使用“写算法”,必须注意确保序列原大小至少不小于我们要求算法写入的元素数目。记住,算法不会执行容器操作,因此它们自身不可能改变容器的大小。
fill(vec.begin(), vec.end(), O); //将每个元素重置为 0
fill(vec.begin(), vec.begin() + vec.size()/2, 10); //将容器的一个子序列设置为10
重排算法
定制操作算法
就是有谓词(回调函数)形参的算法。比如sort默认是元素的<运算,可以传入一个二元谓词返回bool来自定义<运算。
算法的迭代器分类
任何算法的最基本的特性是它要求其迭代器提供哪些操作。如:
find只要求可读、递增,==。
sort要求读写,随机访问。
算法需要的迭代器分了5个级别,高级别的迭代器具备低级别迭代器的所有特性,每个算法都指明了至少需要哪个级别的迭代器。如果传递了一个级别不够的迭代器出错,但编译器可能不会给出警告。
级别 | 迭代器 | 读写 | 扫描 | 运算 |
---|---|---|---|---|
1 | 输入迭代器 | 只读不写 | 单遍扫描 | 只能递增 |
2 | 输出迭代器 | 只写不读 | 单遍扫描 | 只能递增 |
3 | 前向迭代器 | 可读可写 | 多遍扫描 | 只能递增 |
4 | 双向迭代器 | 可读可写 | 多遍扫描 | 可递增递减 |
5 | 随机访问迭代器 | 可读可写 | 多遍扫描 | 全部迭代运算 |
*it++可能导致所有其他指向流的迭代器失效, 那么这个迭代器只能用于单边扫描。 |
- 1、输入迭代器
- 顺序访问。
- ==、!=。
- iter++,++iter。
- *iter,只出现在=右侧。
- 2、输出迭代器
- iter++,++iter。
- *iter,只出现在=左侧。
- 目的迭代器一般都是输出迭代器。
- 3、前向迭代器
- 除了输出迭代器的特性。
- 只能在序列中沿一个方向移动。
- 可以多次读写同 一个元素。
- 4、双向迭代器
- 除了前向迭代器的特性。
- 正向/反向 读写序列中的元素。
- 5、随机访问迭代器
- 除了双向迭代器的特性
- 提供在常量时间内访问序列中任意元素的能力。
- <、<=、>和>=。
- iter+n(、 +=、-和一 )。
- iter1 - iter2。
- iter[n](等价于(*(iter[n]))。
算法形参列表格式
//大部分算法的形参模式如下:
//alg: 算法名字
//beg, end: 第一个范围,输入范围,几乎所有算法都接受
//dest: 算法可以写入的目的位置的迭代器,假定了写多少都安全。
//beg2,end2: 第二个范围,与第一个范围进行一些运算,假定了第二个范围>=第一个范围。
//otherargs: 除了上面,部分算法还需写额外参数
alg(beg, end, otherargs) ;
alg(beg, end, dest, otherargs);
alg(beg, end, beg2, otherargs);
alg(beg, end, beg2, end2, otherargs) ;
算法命名规范
谓词算法(回调)
有回调参数的,一般都有重载
没有回调的版本是用元素的<=或==运算
需要回调的版本都是代替<=或==运算。unique(beg, end); // 使用==运算符比较元素
unique(beg, end, comp); //使用 comp 比较元素
_if算法
接受一个元素值的算法都有。find (beg, end, val ); //查找输入范围中 val 笫一次出现的位置
find_if(beg, end, pred); //查找第一个令 pred 为真的元素
_copy算法
重排元素的算法将重排后的元素写回给定的输入序列中。
这种算法一般有个_copy版本将结果写入指定序列。
reverse (beg, end); //反转输入范围中元素的顺序
reverse_copy(beg, end, dest); //将元素按逆序拷贝到dest
_if and _copy算法
一个目的迭代器 + 一个谓词作迭代器。
//从 vl 中删除奇数元素
remove_if(vl.begin(), vl.end(),[](int i){return i % 2;});
//将偶数元素从 v1 拷贝到 v2 ; v1 不变
remove_copy_if(v1.begin(), v1.end(), back_inserter(v2), [](int i){return i % 2;});
特定容器算法
对立面的就是泛型算法,原因:
- 容器不支持:无法提供需要的迭代器
- 如list、forward_list无法提供sort需要的随机访问迭代器
- 性能问题:可以比泛型算法性能更好
- 如链表的元素交换无需像泛型一样交换值。
可以这么说,特定容器算法是针对性优化了的算法,应该优先使用。
链表特有成员函数算法
//lst2并入lst1,元素将删除。
//lst1和lst2必须有序。
void lst.merge(lst2); //使用<运算符
void lst.merge(lst2,comp); //使用comp谓词
//调用erase删除掉“相等”的元素
void lst.remove(val); //==运算来判定相等
void lst.remove_if(pred); //pred()为true来判定相等
void lst.reverse(); //反转lst中元素的顺序
void lst.sort(); //排序,a<b运算
void lst.sort(comp); //排序,comp(a, b)运算
//调用erase删除同一个值的连续拷贝,重复
void lst.unique(); //==来判定
void lst.unique(pred); //二元谓词pred(a,b)来判定
lst.splice(p, lst2); //lst2的所有元素插入p之前的位置。元素从lst2删除
//lst2类型必须和lst相同。但不是同一个链表
lst.splice(p, lst2, p2); //lst2中p2位置的元素移动到lst中p中位置。
//lst2,lst可以是同一个链表。
lst.splice(p, lst2, b, e); //将lst2中b~e范围的元素移动到lst
//lst2和lst可以是同一个,但p不能在b~e范围。
flst.splice_after(p, lst2); //p指向flst首前位置的迭代器
//lst2的所有元素插入p之后的位置。元素从lst2删除
//lst2类型必须和flst相同。但不是同一个链表
flst.splice_after(p, lst2, p2); //lst2中p2之后的元素移动到flst中p中位置。
//lst2,flst可以是同一个链表。
flst.splice_after(p, lst2, b, e); //将lst2中b~e范围的元素移动到flst的p之后的位置
//lst2和flst可以是同一个,但p不能在b~e范围。