万字长文 | STL 算法总结
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了万字长文 | STL 算法总结相关的知识,希望对你有一定的参考价值。
本篇所有算法源码均已同步收录 GitHub 仓库,欢迎点个小⭐️:https://github.com/rongweihe/CPPNotes/tree/master/STL-source-code-notes
大家好,我是小贺。
上一篇更新了 STL 关联式容器源码,今天我们来学习下 STL 算法。
STL 算法博大精深,涵盖范围之广,其算法之大观,细节之深入,泛型思维之于字里行间,每每阅读都会有不同的收获。
STL 将很多常见的逻辑都封装为现成的算法,熟悉这些算法的使用和实现很多时候可以大大简化编程。
并且在需要的时候能够对 STL 进行扩展,将自定义的容器和算法融入到 STL 中。
侯捷大师在书中说到:深入源码之前,先观察每一个算法的表现和大观,是一个比较好的学习方式。
不多 BB,先上思维导图:
回顾
STL 源码剖析系列:
5 千字长文+ 30 张图解 | 陪你手撕 STL 空间配置器源码
万字长文炸裂!手撕 STL 迭代器源码与 traits 编程技法
超硬核 | 2 万字+20 图带你手撕 STL 序列式容器源码
硬核来袭 | 2 万字 + 10 图带你手撕 STL 关联式容器源码
基本算法
在 STL 标准规格中,并没有区分基本算法或复杂算法,然而 SGI 却把常用的一些算法定义于 <stl_algobase.h>之中,其它算法定义于 <stl_algo.h>中。
常见的基本算法有 equal、fill、fill_n、iter_swap、lexicographical_compare、max、min、mismatch、swap、copy、copy_backward 等。
质变算法和非质变算法
所有的 STL 算法归根到底,都可以分为两类。
所谓“质变算法”是指作用在由迭代器[first,last]所标示出来的区间,上运算过程中会更改区间内的元素内容:
比如拷贝(copy)、互换(swap)、替换(replace)、填写(fill)、删除(remove)、排列组合(permutation)、分割(partition)。随机重排(random shuffling)、排序(sort)等算法,都属于这一类。
而非质变算法是指在运算过程中不会更改区间内的元素内容。比如查找(find),匹配(search)、计数(count)、遍历(for_each)、比较(equal_mismatch)、寻找极值(max,min)等算法。
输入参数
所有泛型算法的前两个参数都是一对迭代器,通过称为 first,last 用来标示算法的操作区间。
每一个 STL 算法的声明,都表现出它所需要的最低程度的迭代器类型。
比如 find() 需要一个 inputiterator ,这是它的最低要求,但同时也可以接受更高类型的迭代器。
如 Forwarditerator、Bidirectionaliterator 或 RandomAcessIterator。
因为,前者都可以看做是一个 inputiterator,而如果你给 find() 传入一个 Outputiterator,会导致错误。
将无效的迭代器传给某个算法,虽然是一种错误,但不保证能够在编译器期间就被捕捉出来。
因为所谓“迭代器类型”并不是真实的型别,它们只是function template的一种型别参数。
许多 STL 算法不仅支持一个版本,往往第一个版本算法会采用默认的行为,另一个版本会提供额外的参数,接受一个仿函数,以便采取其它的策略。
例如 unique() 默认情况下会使用 equality 操作符来比较两个相邻元素,但如果这些元素的型别并没有提供,那么便可以传递一个自定义的函数(或者叫仿函数)。
知道了这一点,对于想要深入研究源码的小伙伴们会更好理解一些。
算法的泛型化
将一个表述完整的算法转化为程序代码,是一个合格程序员的基本功。
如何将算法独立于其所处理的数据结构之外,不受数据的牵绊,使得设计的算法在即将处理的未知的数据结构上(也许是 array,也许是 vector,也许是 list,也许是 deque)上,正确地实现所有操作呢?
这就需要进一步思考:关键在于只要把操作对象的型别加以抽象化,把操作对象的标示法和区间目标的移动行为抽象化,整个算法也就在一个抽象层面上工作了。
这个过程就叫做算法的泛型化,简称泛化。
比如在 STL 源码剖析这本书里举了一个 find 的例子,如果一步步改成 template + 迭代器的形式,来说明了泛化的含义。
下面我们就来看看 STL 那些牛批的算法,限于篇幅,算法的代码没有贴出。
具体源码细节可以去开头的 GitHub 仓库里研究,还有注释哦。
构成
头文件 | 功能 |
| 算法函数 |
| 数值算法 |
| 函数对象/仿函数 |
分类
No. | 分类 | 说明 | |
1 | 非质变算法 | Non-modifying sequence operations | 不直接修改容器内容的算法 |
2 | 质变算法 | Modifying sequence operations | 可以修改容器内容的算法 |
3 | 排序算法 | Sorting/Partitions/Binary search/ | 对序列排序、合并、搜索算法操作 |
4 | 数值算法 | Merge/Heap/Min/max | 对容器内容进行数值计算 |
填充
函数 | 作用 |
| 将值 |
| 将值 |
| 连续调用函数 |
| 连续调用函数 |
-
fill()
/fill_n()
用于填充相同值,generate()
/generate_n()
用于填充不同值。
遍历/变换
函数 | 作用 |
| 将[ |
| 将[ |
| 将[ |
最大最小
函数 | 作用 |
| 返回两个元素中较大一个 |
| 使用自定义比较操作 |
| 返回一个 |
| 使用自定义比较操作 |
| 返回两个元素中较小一个 |
| 使用自定义比较操作 |
| 返回一个 |
| 使用自定义比较操作 |
排序算法(12个):元素排序策略
函数 | 作用 |
| 默认升序重新排列元素 |
| 使用函数 |
| 元素重新排序,使用 |
| 与 |
| 使用函数 |
| 与 |
| 使用函数 |
| 部分排序,被排序元素个数放到[beg,end)内 |
| 使用函数 |
| 与 |
| 使用函数 |
| 单个元素序列重新排序,使所有小于第 |
| 使用函数 |
反转/旋转
函数 | 作用 |
| 元素重新反序排序 |
| 与 |
| 元素移到容器末尾,由 |
| 与 |
随机
函数 | 作用 |
| 元素随机调整次序 |
| 使用函数 |
查找算法(13个):判断容器中是否包含某个值
统计
函数 | 作用 |
| 利用 |
| 使用函数 |
查找
函数 | 作用 |
| 利用 以上是关于万字长文 | STL 算法总结的主要内容,如果未能解决你的问题,请参考以下文章 |