【STL】算法


本文介绍关于标准库算法统一的术语与规范。


目录

  • [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 固定术语定义

  1. in the range [A, B):表示从 A 开始到 B(不包括 B)的零个或多个离散值的序列。
    • 区间 [A, B)
    • 仅当可以从 A 访问 B 时,范围才有效;
    • 从 A 不断自增,有限次后可以抵达 B;
  2. each N in the range [A, B):表示 N 以值 A 开始并递增零次或多次,直至等于值 B。
    • 遍历区间内每一个 N
    • N == B 的情况不在范围内;
  3. the lowest value of N in the range [A, B) such that X:表示确定范围 [A, B) 中的每个 N 是否满足条件 X,直到满足条件 X。
    • 区间 [A, B) 中满足条件 X 的最小 N
    • 从前往后逐个校验元素,找到第一个满足 X 的迭代器;
  4. the highest value of N in the range [A, B) such that X:表示确定范围 [A, B) 中的每个 N 是否满足 X,找到最后一个满足 X 的迭代器。
    • 区间 [A, B) 中满足条件 X 的最大 N
    • 通用逻辑:正向遍历,每次匹配就保存当前 N, 遍历结束返回最后一次匹配的值;
    • 双向 / 随机访问迭代器优化:可从末尾向前倒序查找,直接拿到第一个匹配项;
  5. 表达式如 X - Y,其中 X 和 Y 可以是随机访问迭代器以外的迭代器。
    • 迭代器加减运算(X-Y、X+N、X-N)
    • 即便迭代器不支持原生 - 运算符(如单向迭代器),文档里的加减仅作数学描述;
    • 底层算法会用循环步进实现距离计算,不一定直接调用迭代器的 - 重载;

3 部分规范

3.1 等值二元谓词规范(== 这类相等判断)

有不少算法会使用二元比较谓词(例如 operator==),返回布尔值。

不管是内置的 ==,还是你自定义的等值判断函数,必须遵守三条强制规则:

  1. 绝对不能修改传入的两个入参;
  2. 同一组参数重复调用,返回值永远不变;
  3. 把任意一个参数换成它的副本传入,判断结果完全一致;

3.2 排序/堆算法强制要求:严格弱序

部分算法要求传入的二元判断函数(谓词)能对序列元素建立严格弱序。设比较函数为 pred(X, Y),需要同时满足三条规则:

  1. 严格性:pred(X, X) 的结果必须为假。一个元素不能"小于"自身;
  2. 弱等价:如果 !pred(X, Y) 并且 !pred(Y, X),则认为 X 和 Y 等价;这种等价关系不需要依赖重载 operator==;
  3. 可传递性:如果 pred(X, Y) 成立,且 pred(Y, Z) 成立,那么 pred(X, Z) 一定成立;

3.3 合法 / 非法的严格弱序谓词

这类算法默认使用 X< Y 作为比较规则。其他符合严格弱序的常用谓词:X > Ystd::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))

该有序序列配套的比较函数(无论是默认 < 还是自定义谓词)必须满足约束:

  1. 不得修改两个入参;
  2. 相同参数多次调用,返回值永远不变;
  3. 将任意参数替换为其副本传入,判断结果不变;
  4. 必须满足前文定义的严格弱序;

3.5 什么是基于 < 的堆(大顶堆)

迭代器区间 [First, Last) 满足以下条件时,构成以 operator< 为规则的堆:对区间内下标从1开始的所有元素,都满足 !(*First < *(First + N))

也就是说区间内第一个元素(堆顶)大于等于堆内所有其他元素,也就是大顶堆。

堆的内部存储结构仅 make_heap、pop_heap、push_heap 这三个堆专用模版函数能够识别。和有序序列的要求一致,堆所用比较函数必须:

  1. 不修改传入参数;
  2. 满足严格弱序;
  3. 相同输入返回固定结果;
  4. 参数替换为副本后结果不变;

4 算法头文件

C++标准库的算法分为两个头文件:通用算法在 <algorithm>,数值运算相关算法在 <numeric>

  • <algorithm>:排序、查找、删除、遍历、分区、变换等容器通用操作;
  • <numeric>:累加求和、相邻差值、内积、前缀和等数学计算;