本文介绍关于标准库算法统一的术语与规范。
目录
- [1 概述](#1 概述)
- [2 固定术语定义](#2 固定术语定义)
- [3 部分规范](#3 部分规范)
-
- [3.1 等值二元谓词规范(== 这类相等判断)](#3.1 等值二元谓词规范(== 这类相等判断))
- [3.2 排序/堆算法强制要求:严格弱序](#3.2 排序/堆算法强制要求:严格弱序)
- [3.3 合法 / 非法的严格弱序谓词](#3.3 合法 / 非法的严格弱序谓词)
- [3.4 什么是按 < 升序排列的有序区间](#3.4 什么是按 < 升序排列的有序区间)
- [3.5 什么是基于 < 的堆(大顶堆)](#3.5 什么是基于 < 的堆(大顶堆))
- [4 算法头文件](#4 算法头文件)
1 概述
算法是 C++标准库的基础部分。算法不直接操作容器,而是依托迭代器工作。因此同一套算法几乎能适配所有标准库容器。
2 固定术语定义
in the range [A, B):表示从 A 开始到 B(不包括 B)的零个或多个离散值的序列。
- 区间 [A, B)
- 仅当可以从 A 访问 B 时,范围才有效;
- 从 A 不断自增,有限次后可以抵达 B;
each N in the range [A, B):表示 N 以值 A 开始并递增零次或多次,直至等于值 B。
- 遍历区间内每一个 N
- N == B 的情况不在范围内;
the lowest value of N in the range [A, B) such that X:表示确定范围 [A, B) 中的每个 N 是否满足条件 X,直到满足条件 X。
- 区间 [A, B) 中满足条件 X 的最小 N
- 从前往后逐个校验元素,找到第一个满足 X 的迭代器;
the highest value of N in the range [A, B) such that X:表示确定范围 [A, B) 中的每个 N 是否满足 X,找到最后一个满足 X 的迭代器。
- 区间 [A, B) 中满足条件 X 的最大 N
- 通用逻辑:正向遍历,每次匹配就保存当前 N, 遍历结束返回最后一次匹配的值;
- 双向 / 随机访问迭代器优化:可从末尾向前倒序查找,直接拿到第一个匹配项;
表达式如 X - Y,其中 X 和 Y 可以是随机访问迭代器以外的迭代器。
- 迭代器加减运算(X-Y、X+N、X-N)
- 即便迭代器不支持原生 - 运算符(如单向迭代器),文档里的加减仅作数学描述;
- 底层算法会用循环步进实现距离计算,不一定直接调用迭代器的 - 重载;
3 部分规范
3.1 等值二元谓词规范(== 这类相等判断)
有不少算法会使用二元比较谓词(例如 operator==),返回布尔值。
不管是内置的 ==,还是你自定义的等值判断函数,必须遵守三条强制规则:
- 绝对不能修改传入的两个入参;
- 同一组参数重复调用,返回值永远不变;
- 把任意一个参数换成它的副本传入,判断结果完全一致;
3.2 排序/堆算法强制要求:严格弱序
部分算法要求传入的二元判断函数(谓词)能对序列元素建立严格弱序。设比较函数为 pred(X, Y),需要同时满足三条规则:
- 严格性:pred(X, X) 的结果必须为假。一个元素不能"小于"自身;
- 弱等价:如果 !pred(X, Y) 并且 !pred(Y, X),则认为 X 和 Y 等价;这种等价关系不需要依赖重载 operator==;
- 可传递性:如果 pred(X, Y) 成立,且 pred(Y, Z) 成立,那么 pred(X, Z) 一定成立;
3.3 合法 / 非法的严格弱序谓词
这类算法默认使用 X< Y 作为比较规则。其他符合严格弱序的常用谓词:
X > Y、std::less<T>(X, Y)、std::greater<T>(X, Y)。注:X <= Y、X >= Y 不满足严格弱序,禁止用于排序与堆相关算法。
3.4 什么是按 < 升序排列的有序区间
由迭代器区间 [First, Last) 标识的序列,满足以下条件时,称为按 operator< 升序有序:对任意下标 N(靠前元素)、任意下标 M(靠后元素,M>N),都满足
!(*(First + M) < *(First + N))。该有序序列配套的比较函数(无论是默认 < 还是自定义谓词)必须满足约束:
- 不得修改两个入参;
- 相同参数多次调用,返回值永远不变;
- 将任意参数替换为其副本传入,判断结果不变;
- 必须满足前文定义的严格弱序;
3.5 什么是基于 < 的堆(大顶堆)
迭代器区间 [First, Last) 满足以下条件时,构成以 operator< 为规则的堆:对区间内下标从1开始的所有元素,都满足
!(*First < *(First + N))。也就是说区间内第一个元素(堆顶)大于等于堆内所有其他元素,也就是大顶堆。
堆的内部存储结构仅 make_heap、pop_heap、push_heap 这三个堆专用模版函数能够识别。和有序序列的要求一致,堆所用比较函数必须:
- 不修改传入参数;
- 满足严格弱序;
- 相同输入返回固定结果;
- 参数替换为副本后结果不变;
4 算法头文件
C++标准库的算法分为两个头文件:通用算法在
<algorithm>,数值运算相关算法在<numeric>。
- <algorithm>:排序、查找、删除、遍历、分区、变换等容器通用操作;
- <numeric>:累加求和、相邻差值、内积、前缀和等数学计算;