C++:泛型算法

​操作数据,而非容器

一、概述

泛型算法(Generic Algorithm)​ 是 C++ Standard Template Library (STL) 的核心组成部分,其本质是与容器类型无关的通用操作逻辑。通过模板和迭代器机制,泛型算法能够对任意满足迭代器接口的容器(如 vectorlistmap)进行操作,实现代码复用和高度灵活性

核心特征

  • 容器无关性:不依赖具体容器类型,只要求容器提供迭代器。
  • 类型安全:通过模板参数推导类型,编译期检查错误。
  • 高性能:针对不同容器底层数据结构优化(如 vector 的连续内存访问、list 的链表节点操作)。

STL 泛型算法可分为以下几类

类别典型算法功能描述
非修改序列算法for_eachcount遍历或统计元素,不修改容器
修改序列算法copytransform生成新序列或就地修改元素
排序与划分算法sortpartition重排元素顺序
二分搜索算法lower_boundbinary_search在有序序列中高效查找
集合算法set_unionincludes对有序集合进行交、并、差操作
堆操作算法make_heappush_heap实现堆数据结构操作
数值算法accumulateinner_product数学运算(求和、内积等)

 二、泛型算法的原理

1、泛型算法的核心原则
  1. 算法与容器解耦
    泛型算法不直接操作容器,而是通过迭代器(Iterator)​ 间接访问容器元素。迭代器作为中间层,屏蔽了不同容器的底层实现差异(如 vector 的连续内存、list 的链表节点),使算法能统一处理所有支持迭代器的容器。

  2. 不改变容器结构
    算法永远不会直接添加或删除容器元素,因此不会改变容器的大小​(size)或容量​(capacity)。这一设计保证了算法的通用性和安全性。

    std::vector<int> v = {3, 1, 4, 1, 5};
    auto it = std::find(v.begin(), v.end(), 1); // 查找元素,但不修改容器

​2、算法对元素的操作限制
允许的操作禁止的操作
修改元素的值(如赋值)添加或删除元素
移动元素的位置改变容器的容量或大小
  1. 允许:修改元素值
    算法可以通过迭代器修改元素的值,例如 std::replace 替换指定值:

    std::replace(v.begin(), v.end(), 1, 9); // 将所有1替换为9
  2. 允许:移动元素位置
    算法可以重新排列元素顺序,例如 std::sort 排序:

    std::sort(v.begin(), v.end()); // 元素按升序排列
  3. 禁止:改变容器大小
    算法无法直接调用容器的 insert() 或 erase() 方法。之前有举过std::remove 仅标记要删除的元素,需结合 erase() 完成实际删除的例子:

    auto new_end = std::remove(v.begin(), v.end(), 3); // 移动元素,不改变大小
    v.erase(new_end, v.end()); // 真正删除元素

​3、插入器(Inserter)的桥梁作用(算法不会直接不改变容器结构,只会借助其他工具)
  1. 插入器的功能
    插入器是一种特殊迭代器,允许算法间接向容器添加元素。当对插入器赋值时,它会在底层容器上执行插入操作(如 push_backinsert)。

  2. 插入器类型

    插入器类型底层操作示例
    std::back_inserter调用 push_back()适用于 vectordequelist
    std::front_inserter调用 push_front()适用于 dequelist
    std::inserter调用 insert() 指定位置通用型插入器
  3. 示例:使用插入器实现元素添加

    #include <vector>
    #include <iterator>
    #include <algorithm>int main() {std::vector<int> src = {1, 2, 3};std::vector<int> dest;// 通过back_inserter间接添加元素std::copy(src.begin(), src.end(), std::back_inserter(dest));// dest变为{1, 2, 3}return 0;
    }

​4、泛型算法的工作流程
  1. 输入阶段
    算法接受一对迭代器(beginend)定义操作范围,以及可能的辅助参数(如比较函数、插入器等)。

  2. 执行阶段
    通过迭代器遍历元素,按需修改值或移动位置,但不改变容器结构

  3. 输出阶段
    返回结果可能是一个迭代器(如 find 返回找到的位置)或通过插入器间接操作容器。

流程图


​5、为什么禁止算法直接操作容器?
  1. 通用性:避免算法依赖具体容器的接口(如 vector::push_back 和 list::push_front 不同)。
  2. 安全性:防止误操作导致容器状态不一致(如迭代器失效)。
  3. 性能:允许编译器优化迭代器操作(如连续内存的指针算术)。

​5、泛型算法的核心特性
特性说明
容器无关性通过迭代器抽象,适配所有支持迭代器的容器
元素操作可控可修改值或移动元素,但不改变容器大小
插入器的间接扩展通过特殊迭代器间接添加元素,保持算法逻辑简洁
编译时类型安全模板机制确保操作的类型匹配,避免运行时错误

三、基本泛型算法 

理解算法的最基本的方法就是了解它们是否读取元素、改变元素或是重排元素顺序。 

(1)总纲 

1、泛型算法基本架构

  1. 输入范围机制

    • 双迭代器界定:所有算法通过[begin, end)半开区间操作元素
      sort(vec.begin(), vec.end());  // 对整个vector排序
      find(lst.begin(), lst.end(), 42);  // 在list中查找元素
    • 容器无关性:算法不直接操作容器,仅通过迭代器间接访问
    • 尾后迭代器end()指向最后一个元素的后一位,确保空范围安全
  2. 参数通用模式

    algorithm(first_iter, last_iter, [predicate/op]);
    // 示例:统计大于5的元素
    count_if(v.begin(), v.end(), [](int x){return x > 5;});

2、算法行为分类

类型特点典型算法
观察型不修改元素,仅读取findcountaccumulate
修改型直接修改元素值replacefillcopy
重排型改变元素顺序但不修改值sortreverseshuffle
// 观察型:查找元素位置
auto pos = find(v.begin(), v.end(), target);// 修改型:将所有负数替换为0
replace_if(v.begin(), v.end(), [](int x){return x < 0;}, 0);// 重排型:按自定义规则排序
sort(v.begin(), v.end(), [](int a, int b){return a%10 < b%10;});
  1. 可组合性

    // 组合排序与查找
    sort(v.begin(), v.end());
    auto it = lower_bound(v.begin(), v.end(), 42);

4、一些建议

  1. 掌握共性规律

    • 所有算法都遵循操作范围->处理元素->返回结果的流程
    • 70%算法可归入"读/改/排"三大类
  2. 关注关键参数

    • 前两个参数必为输入范围
    • 可选参数包括:谓词(判断条件)、输出位置、自定义操作函数
  3. 实践建议

    // 从简单算法入手理解模式
    vector<int> data {5,3,7,2};// 1. 使用find_if查找首个偶数
    auto it = find_if(data.begin(), data.end(), [](int x){return x%2==0;});// 2. 用transform转换元素
    transform(data.begin(), data.end(), data.begin(), [](int x){return x*2;});// 3. 结合sort和unique去重
    sort(data.begin(), data.end());
    auto last = unique(data.begin(), data.end());
    data.erase(last, data.end());

(2)基本泛型算法

 2.1观察型算法(只读)

核心定义

本质:仅读取输入范围内的元素,​不修改容器内容或元素顺序的算法类型
设计目标:通过统一接口实现数据的安全观察与计算


核心特征
  1. 无副作用性

    • 算法执行后保证原始数据完全不变
    • 适用于需要保留原始数据场景(如统计分析、条件检测)
  2. 输入范围依赖

    • 通过[begin, end)迭代器对操作数据范围
    • 典型参数结构:
      result_type algorithm(first_iter, last_iter, [predicate]);
  3. 谓词扩展性

    • 支持通过谓词(Predicate)自定义读取逻辑
      // 统计字符串长度大于5的元素
      count_if(vec.begin(), vec.end(), [](const string& s){return s.size() >5;}); 

常见算法分类
算法类型典型代表返回值类型
查找型findsearchadjacent_find迭代器(指向目标位置)
计数型countcount_if整型数值(统计结果)
比较型equalmismatchbool或差异位置对
累积型accumulateinner_product计算值(如求和/内积)
遍历型for_each可选的函数对象返回值

关键使用模式
// 示例1:查找首个满足条件的元素
vector<int> data {7, 4, 9, 2};
auto it = find_if(data.begin(), data.end(), [](int x){return x%3 ==0;});  // 返回指向9的迭代器// 示例2:跨容器元素比较
list<string> names {"Alice", "Bob"};
vector<string> tmp {"Alice", "Charlie"};
bool match_front = equal(names.begin(), names.end(), tmp.begin());  // 返回false// 示例3:数值计算
int sum = accumulate(data.begin(), data.end(), 0);  // 7+4+9+2=22

设计原则体现
  1. 统一性
    所有只读算法遵循相同的迭代器接口规范,例如:

    // 所有查找类算法返回迭代器
    auto pos1 = find(...);     // 值查找
    auto pos2 = search(...);   // 子序列查找
  2. 泛型扩展
    通过模板支持任意元素类型:

    // 对自定义类型有效
    struct Person { string name; int age; };
    vector<Person> people;
    auto elder = find_if(people.begin(), people.end(),[](const Person& p){return p.age >60;});
  3. 安全保证
    即使操作无效范围也不会导致数据破坏:

    vector<int> empty_vec;
    // 安全操作:返回empty_vec.end()
    auto result = find(empty_vec.begin(), empty_vec.end(), 42); 

应用场景建议
  1. 数据质量检查

    // 验证容器中是否存在非法值
    bool has_negative = any_of(data.begin(), data.end(),[](int x){return x <0;});
  2. 元数据分析

    // 计算文本行平均长度
    vector<string> lines = read_file();
    int total = accumulate(lines.begin(), lines.end(), 0,[](int sum, const string& s){return sum + s.size();});
    double avg = static_cast<double>(total)/lines.size();
  3. 预处理验证

    // 排序前检查是否已有序
    if(!is_sorted(data.begin(), data.end())) {sort(data.begin(), data.end());
    }

注意事项
  1. 迭代器有效性
    确保输入的迭代器范围[begin, end)构成有效区间

  2. 谓词纯度
    谓词函数应当是无副作用的纯函数(不修改外部状态)

  3. 性能选择
    对已排序数据优先使用binary_search等优化算法:

    // 比线性查找更高效
    bool exists = binary_search(sorted_data.begin(), sorted_data.end(), target);

2.2修改型算法 (算法不检查写操作)

核心概念
  1. 定义
    通过迭代器修改容器元素值或结构的算法,属于修改型算法

    • 直接修改:在原容器上改变元素值(如fill
    • 输出写入:将结果写入另一个容器(如transform
  2. 统一接口特征

    // 典型参数结构
    algorithm(input_begin, input_end, [output_iter], [operation]);

常见算法分类
算法类型典型代表修改方式关键特性
原位修改型fillreplacegenerate直接修改输入范围内的元素无需额外存储空间
拷贝输出型copytransform将结果写入另一个容器需要预分配输出空间
条件修改型replace_ifremove_if根据谓词选择性修改元素常配合lambda使用
结构变更型removeunique逻辑删除后需配合erase实际修改容器物理结构
详细解释

1. 填充与赋值

算法功能示例
std::fill将区间填充为指定值fill(v.begin(), v.end(), 0)
std::generate通过函数生成值填充区间generate(v.begin(), v.end(), rand)
std::iota填充递增序列(C++11 起)iota(v.begin(), v.end(), 1)

2. 元素转换

算法功能示例
std::transform对每个元素应用函数并写入目标位置transform(src.begin(), src.end(), dest.begin(), toupper)
std::replace替换区间内的特定值replace(v.begin(), v.end(), 3, 10)
std::replace_if按条件替换元素replace_if(v.begin(), v.end(), is_odd, 0)

3. 删除与去重

算法功能示例
std::remove移除指定值(需配合 erasev.erase(remove(v.begin(), v.end(), 42), v.end())
std::remove_if按条件移除元素v.erase(remove_if(v.begin(), v.end(), is_negative), v.end())
std::unique移除相邻重复元素(需先排序)v.erase(unique(v.begin(), v.end()), v.end())

4. 重新排列

算法功能示例
std::reverse反转区间元素顺序reverse(v.begin(), v.end())
std::rotate循环移动区间元素rotate(v.begin(), v.begin()+2, v.end())
std::shuffle随机打乱元素顺序(需随机引擎)shuffle(v.begin(), v.end(), rand_gen)

5. 复制与移动

算法功能示例
std::copy复制源区间到目标位置copy(src.begin(), src.end(), dest.begin())
std::move移动元素到目标位置(C++11 起)move(src.begin(), src.end(), dest.begin())

使用场景

  1. 初始化容器
    使用 fillgenerate 或 iota 填充初始值。

  2. 数据清洗
    通过 removeremove_if 或 unique 删除无效或冗余数据。

  3. 数据转换
    使用 transform 或 replace 修改元素格式或内容。

  4. 算法优化
    通过 reverserotate 或 shuffle 调整数据顺序以满足特定需求。

  5. 数据迁移
    使用 copy 或 move 将数据转移到其他容器。

核心写入方式
1. 原位直接修改
vector<int> vec(5); 
fill(vec.begin(), vec.end(), 10);  // 所有元素变为10
replace(vec.begin(), vec.end(), 10, 20); // 10替换为20
2. 输出到其他容器
vector<int> src {1,2,3}, dest;
// 需要确保dest有足够空间(或使用插入迭代器)
copy(src.begin(), src.end(), back_inserter(dest)); // 转换+写入模式
transform(src.begin(), src.end(), back_inserter(dest),[](int x){return x*2;});  // dest得到[2,4,6]
3. 条件性修改
vector<int> data {5,-3,7,-2};
replace_if(data.begin(), data.end(), [](int x){return x <0;}, 0);  // 负数替换为0auto new_end = remove_if(data.begin(), data.end(),[](int x){return x%2 ==0;}); // 逻辑删除偶数
data.erase(new_end, data.end());  // 物理删除
关键注意事项
  1. 空间预分配

    // 错误:未预分配空间
    vector<int> dest;  
    copy(src.begin(), src.end(), dest.begin()); // 崩溃!// 正确:使用插入迭代器
    copy(src.begin(), src.end(), back_inserter(dest));
  2. 迭代器失效

    vector<int> data {1,2,3,4};
    auto it = data.begin();
    fill(data.begin(), data.end(), 0);  // 安全操作
    data.push_back(5);                  // 使it可能失效
  3. 谓词副作用

    // 错误示例:带副作用的谓词
    int counter = 0;
    replace_if(data.begin(), data.end(),[&](int x){return ++counter >2;}, 0); // 结果不可预测

典型应用模式
// 数据清洗管道
vector<int> process_data(vector<int>& raw) {vector<int> result;// 1. 去除非正数remove_copy_if(raw.begin(), raw.end(), back_inserter(result),[](int x){return x <=0;});// 2. 数值规范化transform(result.begin(), result.end(), result.begin(),[](int x){return x*100;});// 3. 去重处理sort(result.begin(), result.end());auto last = unique(result.begin(), result.end());result.erase(last, result.end());return result;
}

(3)重排型容器算法

1、重排型算法概述

重排型算法通过重新排列容器内元素的顺序实现特定目标,不改变元素值。这些算法定义在头文件 <algorithm> 中,部分需要 <random> 支持。核心特点如下:

  • 不改变容器大小​(除非配合erase
  • 依赖迭代器范围,支持不同容器
  • 时间复杂度各异,需根据场景选择
类别算法列表用法说明
排序(Sorting)sortstable_sortpartial_sortsort(first, last):对迭代器范围 [first, last) 内的元素进行排序。
stable_sort(first, last):稳定排序,相同元素相对顺序不变。
partial_sort(first, middle, last):部分排序,使 [first, middle) 有序,剩余元素无序。
反转(Reversing)reversereverse(first, last):反转迭代器范围 [first, last) 内元素的顺序。
旋转(Rotating)rotaterotate_copyrotate(first, middle, last):将 [middle, last) 区间的元素移到 [first, last) 最前方。
rotate_copy(first, middle, last, dest):旋转复制,结果存入 dest 开始的位置。
随机重排(Shuffling)shuffleshuffle(first, last, rng):利用随机数生成器 rng(如 default_random_engine)对 [first, last) 内元素随机重排。
排列生成(Permutation)next_permutationprev_permutationnext_permutation(first, last):求下一个字典序排列,成功返回 true,否则 false
prev_permutation(first, last):求上一个字典序排列,成功返回 true,否则 false
分区(Partitioning)partitionstable_partitionpartition(first, last, pred):按谓词 pred 分区,不保证元素相对顺序。
stable_partition(first, last, pred):稳定分区,保留元素相对顺序。

2.关键算法详解

1. 排序算法
  • std::sort

    • 功能:对范围内的元素进行升序排序(默认)或自定义排序。

    • 实现原理:通常为IntroSort(快速排序 + 堆排序的混合)。

    • 时间复杂度:平均 O(n log n),最坏 O(n²)

    • 示例

      #include <vector>
      #include <algorithm>std::vector<int> vec = {3, 1, 4, 1, 5, 9, 2, 6};
      std::sort(vec.begin(), vec.end());  // 默认升序
      std::sort(vec.begin(), vec.end(), std::greater<int>()); // 降序
  • std::stable_sort

  • 示例
    struct Item { int id; int value; };
    std::vector<Item> items = {{1,3}, {2,3}, {3,1}};
    std::stable_sort(items.begin(), items.end(), [](const Item& a, const Item& b) {return a.value < b.value;  // 按 value 排序,相同值保持插入顺序
    });
    // 结果顺序:{3,1}, {1,3}, {2,3}
  • 注意:时间复杂度 O(n log n),内存占用较高。
  • 特点:保持相等元素的原始相对顺序。
  • 适用场景:需要稳定排序时(如按多字段排序)。
std::partial_sort
  • 用途:部分排序(仅排序前 N 个元素)
  • 示例
    std::vector<int> v = {9, 5, 2, 7, 4};
    // 仅排序前3个元素,其余不保证顺序
    std::partial_sort(v.begin(), v.begin() + 3, v.end());
    // 结果:2,4,5,9,7(前3位有序,后两位无序)
  • 注意:时间复杂度 O(n log k)(k 为排序部分大小)。

2. 反转算法
  • std::reverse

    • 功能:反转容器中元素的顺序。

    • 实现原理:交换首尾对称位置的元素。

    • 时间复杂度O(n)

    • 示例

      std::vector<int> vec = {1, 2, 3, 4, 5};
      std::reverse(vec.begin(), vec.end());  // 结果为 {5, 4, 3, 2, 1}

3. 旋转算法
  • std::rotate

    • 功能:将 [first, middle, last) 范围内的元素左旋,使 middle 成为新的第一个元素。

    • 时间复杂度O(n)

    • 示例

      std::vector<int> vec = {1, 2, 3, 4, 5};
      auto mid = vec.begin() + 2;  // 指向元素3
      std::rotate(vec.begin(), mid, vec.end());  // 结果 {3, 4, 5, 1, 2}
 std::rotate_copy
  • 用途:旋转并拷贝到新容器,不修改原数据
  • 示例
    std::vector<int> src = {1,2,3,4,5};
    std::vector<int> dest(src.size());
    std::rotate_copy(src.begin(), src.begin()+2, src.end(), dest.begin());
    // dest: 3,4,5,1,2(原容器不变)

4. 随机重排算法
  • std::shuffle

    • 功能:随机打乱元素顺序。

    • 依赖:需提供随机数生成器(如 std::mt19937)。

    • 示例

      #include <random>
      std::vector<int> vec = {1, 2, 3, 4, 5};
      std::random_device rd;
      std::mt19937 rng(rd());
      std::shuffle(vec.begin(), vec.end(), rng);
  • 注意:使用高质量随机数生成器(如 mt19937)。

5. 排列生成算法
  • std::next_permutation

    • 功能:按字典序生成下一个更大的排列。

    • 返回值bool(若存在更小排列则返回 false)。

    • 示例

      #include <algorithm>
      #include <vector>
      #include <iostream>
      int main() {std::vector<int> v = {1,2,3};
      do {// 依次输出:123, 132, 213, 231, 312, 321for (auto i : v) {std::cout << i;}std:: cout<<std::endl;
      } while (std::next_permutation(v.begin(), v.end()));
      }
std::prev_permutation
  • 用途:生成下一个字典序更小的排列
  • 示例
    #include <algorithm>
    #include <vector>
    #include <iostream>
    int main() {std::vector<int> v = {1,2,3};
    do {// 依次输出:123, 132, 213, 231, 312, 321for (auto i : v) {std::cout << i;}std:: cout<<std::endl;
    } while (std::next_permutation(v.begin(), v.end()));std::vector<int> v1 = {3,2,1};
    do {// 依次输出:321, 312, 231, 213, 132, 123for(auto i : v1) {std::cout << i;}std::cout << std::endl;
    } while (std::prev_permutation(v1.begin(), v1.end()));return 0;
    }

6. 分区算法
  • std::partition

    • 功能:根据谓词条件将元素分为满足条件和不满足条件的两组,并将满足条件的元素移到前端。

    • 不保证稳定性(相等元素可能交换顺序)。

    • 示例

      std::vector<int> vec = {1, 2, 3, 4, 5};
      auto it = std::partition(vec.begin(), vec.end(), [](int x) { return x % 2 == 0; });
      // 偶数在前,奇数在后,如 {4, 2, 3, 1, 5}
      std::stable_partition
    • 用途:分区并保持元素相对顺序
    • 示例
      #include <iostream>
      #include <vector>
      #include <algorithm>int main() {std::vector<int> v = {1,2,3,4,1,5};auto it = std::stable_partition(v.begin(), v.end(), [](int x) { return x < 3; });// 结果:1,2,1,3,4,5 → 1,2 保持顺序,剩余元素保持顺序for (auto i : v) {std::cout << i << " ";}return 0;
      }
    • 注意:时间复杂度 O(n),但可能使用额外内存。 

3.总结与选择建议

算法类型典型场景性能与特点
sort需要快速排序,不关心相等元素顺序O(n log n),非稳定
stable_sort需保持相等元素原始顺序(如多字段排序)O(n log n),内存占用较高
partial_sort仅需前 N 个元素有序(如排行榜前10名)O(n log k),k 为部分排序大小
rotate循环移动元素(如缓冲区处理)O(n)
shuffle随机化数据(如游戏洗牌)O(n),依赖随机数生成器质量
next_permutation遍历所有排列(如密码破解、组合优化)O(n) 单次调用
stable_partition分区且需保持元素顺序(如日志按级别分类)O(n),可能使用额外内存

 四、如何去“定制”自己的算法呢?

1. 默认比较与自定义操作的必要性

C++标准库算法如sort默认使用<运算符比较元素。但在以下情况需自定义比较逻辑:

  • 默认排序规则不适用:如按字符串长度而非字典序排序。
  • 元素类型未定义<运算符:如自定义结构体需按特定成员排序。

2. 向算法传递函数:以sort为例

sort的重载版本允许传入谓词(Predicate)​,自定义排序规则。

示例1:按字符串长度排序

#include <vector>
#include <algorithm>
#include <string>
#include <iostream>
#include <string_view>/*** @brief 比较两个字符串视图的长度,判断第一个字符串是否比第二个字符串短。* * @param first 第一个要比较的字符串视图。* @param second 第二个要比较的字符串视图。* @return true 如果 first 的长度小于 second 的长度。* @return false 如果 first 的长度大于或等于 second 的长度。*/
// 自定义比较函数(二元谓词)
bool isShorter(const std::string_view first, const std::string_view second) {return first.size() < second.size();
}int main() {std::vector<std::string> vec = {"apple", "banana", "cherry", "date"};std::sort(vec.begin(), vec.end(), isShorter); // 按长度升序排列for (const auto &str : vec) {std::cout << str << " (" << str.size() << "), ";}// 结果:date (4), apple (5), banana (6), cherry (6)return 0;
}

3. 谓词的定义与分类

  • 谓词:返回bool的可调用对象(函数、函数对象、Lambda)。
    • 一元谓词:接受一个参数,如find_if的条件判断。
    • 二元谓词:接受两个参数,如sort的比较操作。

示例2:自定义结构体排序

struct Person {std::string name;int age;
};// 按年龄升序的二元谓词
/*** @brief 比较两个 Person 对象的年龄,判断第一个对象的年龄是否小于第二个对象的年龄。* * @param a 第一个要比较的 Person 对象的常量引用。* @param b 第二个要比较的 Person 对象的常量引用。* @return true 如果 a 的年龄小于 b 的年龄。* @return false 如果 a 的年龄大于或等于 b 的年龄。*/
#include <cassert>bool compareByAge(const Person &a, const Person &b, bool ascending = true) {if (ascending) {return a.age < b.age;}return a.age > b.age;
}int main() {std::vector<Person> people = {{"Alice", 30}, {"Bob", 25}, {"Charlie", 20}};// 升序排序std::sort(people.begin(), people.end(), [](const Person &a, const Person &b) {return compareByAge(a, b, true);});// 降序排序std::sort(people.begin(), people.end(), [](const Person &a, const Person &b) {return compareByAge(a, b, false);});return 0;
}

4. 严格弱序条件

自定义二元谓词必须满足严格弱序,确保排序逻辑合理:

  1. 非自反性comp(a, a) == false
  2. 不对称性:若comp(a, b) == true,则comp(b, a) == false
  3. 传递性:若comp(a, b) && comp(b, c),则comp(a, c)
  4. 等价传递性:若ab等价,bc等价,则ac等价。

错误示例:使用<=破坏严格弱序

// 错误:违反非自反性和不对称性
bool badCompare(int a, int b) {return a <= b; // 若a == b,返回true,导致未定义行为
}

5. 使用Lambda简化谓词

对于简单逻辑,Lambda表达式更便捷。

示例3:Lambda按结构体成员排序

struct Point { int x, y; };int main() {std::vector<Point> points = {{3, 5}, {1, 2}, {2, 4}};// 按 x 坐标升序排序std::sort(points.begin(), points.end(), [](const Point &a, const Point &b) { // 比较两个点的 x 坐标return a.x < b.x; });// 结果:{1,2}, {2,4}, {3,5}return 0;
}

6.小结

  • 自定义排序:通过传递二元谓词,覆盖sort的默认<比较。
  • 严格弱序:确保谓词满足条件,避免未定义行为。
  • 灵活选择:函数、Lambda、函数对象均可作为谓词,按需使用。

再见咯

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/40079.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

UE4学习笔记 FPS游戏制作20 重写机器人和玩家死亡 切换相机和模型

定义父类中的死亡方法 在父类中定义OnDie方法&#xff0c;不需要实现&#xff0c;由子类实现各自的死亡逻辑 新建一个Die方法&#xff0c;处理公共的死亡逻辑 Die的实现&#xff1a; 以前的分离控制现在要延迟做&#xff0c;如果分离了控制器&#xff0c;就无法再获取到玩家的…

AI小白的第七天:必要的数学知识(概率)

概率 Probability 1. 概率的定义 概率是一个介于 0 和 1 之间的数&#xff0c;表示某个事件发生的可能性&#xff1a; 0&#xff1a;事件不可能发生。1&#xff1a;事件必然发生。0 到 1 之间&#xff1a;事件发生的可能性大小。 例如&#xff0c;掷一枚公平的硬币&#xf…

Occlum 是一个内存安全的、支持多进程的 library OS,特别适用于 Intel SGX。

前言 大家好&#xff0c;我是老马。 sofastack 其实出来很久了&#xff0c;第一次应该是在 2022 年左右开始关注&#xff0c;但是一直没有深入研究。 最近想学习一下 SOFA 对于生态的设计和思考。 sofaboot 系列 SOFABoot-00-sofaboot 概览 SOFABoot-01-蚂蚁金服开源的 s…

【day1】数据结构刷题 链表

一 反转链表 206. 反转链表 给你单链表的头节点 head &#xff0c;请你反转链表&#xff0c;并返回反转后的链表。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5] 输出&#xff1a;[5,4,3,2,1]示例 2&#xff1a; 输入&#xff1a;head [1,2] 输出&#xff1a;[2,1]…

canvas学习:如何绘制带孔洞的多边形

在canvas中可以通过路径绘制多边形&#xff0c;但是多边形有一种特殊的情况就是带有孔洞的多边形。这种多边形又该如何绘制呢&#xff0c;今天我就来探究一下这个问题 一、使用通常的方法绘制&#xff08;失败&#xff09; 我准备了如下的两组坐标&#xff0c;outer构成了多边…

Linux信号的诞生与归宿:内核如何管理信号的生成、阻塞和递达?

个人主页&#xff1a;敲上瘾-CSDN博客 个人专栏&#xff1a;Linux学习、游戏、数据结构、c语言基础、c学习、算法 目录 一、认识信号 二、信号的产生 1.键盘输入 2.系统调用 3.系统指令 4.硬件异常 5.软件条件 三、信号的保存 1.block 2.pending 3.handler 四、信号…

阳台光伏新守护者:电流传感器助力安全发电

安科瑞顾强 插即用光伏&#xff08;Plug-In Solar PV&#xff09;以其便捷的安装方式和亲民的准入标准&#xff0c;正在推动欧洲能源结构的革新性转变。根据SolarPower Europe发布的最新行业报告显示&#xff0c;预计到2025年&#xff0c;仅德国通过认证的即插即用光伏系统注册…

【工程记录】QwQ-32b 8bit量化部署教程(vLLM | 缓解复读)

文章目录 写在前面1. 环境配置2. 下载QwQ-32b 8bit量化模型3. 使用vLLM本地推理 写在前面 仅作个人学习记录用。本文记录QwQ-32b 8bit量化模型的部署的详细方法。 1. 环境配置 以下环境经测试无bug&#xff08;Deepseek R1用这个环境也能直接跑&#xff09;&#xff1a; gp…

Elasticsearch 入门

Elasticsearch 入门 1. 认识 Elasticsearch 1.1 现有查询数据存在的问题 查询效率较低 由于数据库模糊查询不走索引&#xff0c;在数据量较大的时候&#xff0c;查询性能很差。 功能单一 数据库的模糊搜索功能单一&#xff0c;匹配条件非常苛刻&#xff0c;必须恰好包含用户…

Docker镜像相关命令(Day2)

文章目录 前言一、问题描述二、相关命令1.查看镜像2.搜索镜像3.拉取镜像4.删除镜像5.镜像的详细信息6.标记镜像 三、验证与总结 前言 Docker 是一个开源的容器化平台&#xff0c;它让开发者能够将应用及其依赖打包到一个标准化的单元&#xff08;容器&#xff09;中运行。在 D…

网站服务器常见的CC攻击防御秘籍!

CC攻击对网站的运营是非常不利的&#xff0c;因此我们必须积极防范这种攻击&#xff0c;但有些站长在防范这种攻击时可能会陷入误区。让我们先了解下CC攻击&#xff01; CC攻击是什么 CC是DDoS攻击的一种&#xff0c;CC攻击是借助代理服务器生成指向受害主机的合法请求&#x…

【PICO】开发环境配置准备

Unity编辑器配置 安装Unity编辑器 安装UnityHub 安装Unity2021.3.34f1c1 添加安卓平台模块 Pico软件资源准备 资源准备地址&#xff1a;Pico Developer PICO SDK PICO Unity Integration SDK PICO Unity Integration SDK 为 PICO 基于 Unity 引擎研发的软件开发工具…

传输层安全协议 SSL/TLS 详细介绍

传输层安全性协议TLS及其前身安全套接层SSL是一种安全传输协议&#xff0c;目前TLS协议已成为互联网上保密通信的工业标准&#xff0c;在浏览器、邮箱、即时通信、VoIP等应用程序中得到广泛的应用。本文对SSL和TLS协议进行一个详细的介绍&#xff0c;以便于大家更直观的理解和认…

一文解读DeepSeek在工业制造领域的应用

引言 在当今数字化浪潮席卷全球的背景下&#xff0c;各个行业都在积极寻求创新与变革&#xff0c;工业制造领域也不例外。然而&#xff0c;传统工业制造在生产效率、质量控制、成本管理等方面面临着诸多挑战。在这一关键时期&#xff0c;人工智能技术的兴起为工业制造带来了新的…

3.Excel:快速分析

补充&#xff1a;快捷键&#xff1a;CTRLQ 一 格式化 1.数据条 2.色阶 3.开始菜单栏里面选择更多 补充&#xff1a;想知道代表什么意思&#xff1a;管理规则-编辑规则 二 表格 点击后会变成超级表&#xff0c;之前是普通表。 三 迷你图 图放在单元格里面。 补充&#xff1a;除了…

区间端点(java)(贪心问题————区间问题)

deepseek给了一种超级简单的做法 我是真的想不到 贪心的思路是 局部最优——>全局最优 这种我是真的没有想到&#xff0c;这样的好处就是后面便利的时候可以通过foreach循环直接便利qu的子元素也就是对应的某一个区间, 将一个二维数组变成一维数组&#xff0c;每一个一维…

STM32蜂鸣器播放音乐

STM32蜂鸣器播放音乐 STM32蜂鸣器播放音乐 Do, Re, Mi, Fa, 1. 功能概述 本系统基于STM32F7系列微控制器&#xff0c;实现了以下功能&#xff1a; 通过7个按键控制蜂鸣器发声&#xff0c;按键对应不同的音符。每个按键对应一个音符&#xff08;Do, Re, Mi, Fa, Sol, La, Si&a…

基于docker-compose 部署可道云资源管理器

容器编排Explorer 容器化部署MariaDB容器化部署Redis容器化部署PHP容器化部署Nginx编排部署compose服务 var code “9861ce02-1202-405b-b419-4dddd337aaa7” GitHub官网 KodExplorer 是一款网页文件管理器。它也是一个网页代码编辑器&#xff0c;可让你直接在网页浏览器中开…

【Git】--- Git远程操作 标签管理

Welcome to 9ilks Code World (๑•́ ₃ •̀๑) 个人主页: 9ilk (๑•́ ₃ •̀๑) 文章专栏&#xff1a; Git 前面我们学习的操作都是在本地仓库进行了&#xff0c;如果团队内多人协作都在本地仓库操作是不行的&#xff0c;此时需要新的解决方案 --- 远程仓库。…

Deepseek API+Python 测试用例一键生成与导出 V1.0.3

** 功能详解** 随着软件测试复杂度的不断提升,测试工程师需要更高效的方法来设计高覆盖率的测试用例。Deepseek API+Python 测试用例生成工具在 V1.0.3 版本中,新增了多个功能点,优化了提示词模板,并增强了对文档和接口测试用例的支持,极大提升了测试用例设计的智能化和易…