Lambda表达式在算法竞赛中的应用

Lambda 表达式在算法竞赛中的应用

一、什么是 Lambda 表达式?

Lambda 表达式是 C++11 引入的匿名函数特性,允许在代码中直接定义简短的函数,而无需单独声明和定义。

基本语法

cpp 复制代码
[capture](parameters) -> return_type {
    // 函数体
}
部分 说明 示例
capture 捕获列表,用于访问外部变量 [][&][=][x]
parameters 参数列表 (int a, int b)
return_type 返回类型(可省略) -> int
函数体 具体逻辑 { return a + b; }

简单示例

cpp 复制代码
auto add = [](int a, int b) {
    return a + b;
};

cout << add(3, 5) << "\n";  // 输出 8

二、Lambda 在 sort 中的应用

1. 基础排序

cpp 复制代码
vector<int> a = {5, 2, 8, 1, 9, 3};

// 升序(默认)
sort(a.begin(), a.end());

// 降序
sort(a.begin(), a.end(), [](int x, int y) {
    return x > y;
});

2. 结构体多关键字排序

cpp 复制代码
struct Student {
    string name;
    int score;
    int age;
};

vector<Student> stu = {
    {"Alice", 90, 20},
    {"Bob", 85, 21},
    {"Charlie", 90, 19}
};

// 按分数降序,分数相同时按年龄升序
sort(stu.begin(), stu.end(), [](const Student& a, const Student& b) {
    if (a.score != b.score) {
        return a.score > b.score;  // 分数高的在前
    }
    return a.age < b.age;  // 年龄小的在前
});

3. pair 排序

cpp 复制代码
vector<pair<int, int>> p = {{1, 5}, {2, 3}, {1, 2}, {3, 1}};

// 第一关键字升序,第二关键字降序
sort(p.begin(), p.end(), [](const pair<int, int>& a, const pair<int, int>& b) {
    if (a.first != b.first) {
        return a.first < b.first;
    }
    return a.second > b.second;
});

4. 区间调度问题(贪心)

cpp 复制代码
// 按结束时间升序排序
vector<pair<int, int>> intervals = {{1, 3}, {2, 5}, {3, 6}, {5, 7}};

sort(intervals.begin(), intervals.end(), [](const pair<int, int>& a, const pair<int, int>& b) {
    return a.second < b.second;
});

三、Lambda 在 priority_queue 中的应用

1. 基础用法

cpp 复制代码
// 大根堆(默认)
priority_queue<int> maxHeap;

// 小根堆
priority_queue<int, vector<int>, greater<int>> minHeap;

2. 自定义比较函数

cpp 复制代码
// pair 优先队列:按 first 升序(小根堆)
auto cmp = [](const pair<int, int>& a, const pair<int, int>& b) {
    return a.first > b.first;  // 注意:priority_queue 返回 true 表示优先级低
};
priority_queue<pair<int, int>, vector<pair<int, int>>, decltype(cmp)> pq(cmp);

pq.push({1, 5});
pq.push({2, 3});
pq.push({1, 2});

cout << pq.top().first << " " << pq.top().second << "\n";  // 1 2

3. 结构体优先队列

cpp 复制代码
struct Task {
    string name;
    int priority;
    int deadline;
};

// 按优先级降序,优先级相同时按截止时间升序
auto cmpTask = [](const Task& a, const Task& b) {
    if (a.priority != b.priority) {
        return a.priority < b.priority;  // 优先级低的沉底
    }
    return a.deadline > b.deadline;  // 截止时间晚的沉底
};
priority_queue<Task, vector<Task>, decltype(cmpTask)> taskPQ(cmpTask);

4. Dijkstra 算法

cpp 复制代码
// 小根堆:{距离, 节点}
priority_queue<pair<long long, int>, vector<pair<long long, int>>, greater<pair<long long, int>>> pq;

四、Lambda 在其他 STL 算法中的应用

1. find_if

cpp 复制代码
vector<int> a = {1, 2, 3, 4, 5};

// 查找第一个大于 3 的元素
auto it = find_if(a.begin(), a.end(), [](int x) {
    return x > 3;
});

if (it != a.end()) {
    cout << "找到: " << *it << "\n";  // 找到: 4
}

2. count_if

cpp 复制代码
vector<int> a = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

// 统计偶数个数
int cnt = count_if(a.begin(), a.end(), [](int x) {
    return x % 2 == 0;
});

cout << "偶数个数: " << cnt << "\n";  // 5

3. remove_if

cpp 复制代码
vector<int> a = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

// 删除所有偶数
a.erase(remove_if(a.begin(), a.end(), [](int x) {
    return x % 2 == 0;
}), a.end());

// a 现在是 {1, 3, 5, 7, 9}

4. transform

cpp 复制代码
vector<int> a = {1, 2, 3, 4, 5};
vector<int> b(a.size());

// 每个元素平方
transform(a.begin(), a.end(), b.begin(), [](int x) {
    return x * x;
});

// b 现在是 {1, 4, 9, 16, 25}

5. for_each

cpp 复制代码
vector<int> a = {1, 2, 3, 4, 5};

// 打印每个元素
for_each(a.begin(), a.end(), [](int x) {
    cout << x << " ";
});
cout << "\n";

6. all_of / any_of / none_of

功能:判断范围内元素是否满足特定条件

算法 功能 返回值
all_of 是否所有元素都满足条件 全是 true 返回 true
any_of 是否存在元素满足条件 有一个 true 就返回 true
none_of 是否没有元素满足条件 全是 false 返回 true

时间复杂度O(n),但找到结果后会提前退出

示例

cpp 复制代码
vector<int> a = {2, 4, 6, 8, 10};

// 是否全是偶数
bool allEven = all_of(a.begin(), a.end(), [](int x) {
    return x % 2 == 0;
});
cout << allEven << "\n";  // 1 (true)

// 是否有大于 5 的元素
bool hasBig = any_of(a.begin(), a.end(), [](int x) {
    return x > 5;
});
cout << hasBig << "\n";  // 1 (true)

// 是否没有奇数
bool noOdd = none_of(a.begin(), a.end(), [](int x) {
    return x % 2 != 0;
});
cout << noOdd << "\n";  // 1 (true)

提前退出机制

cpp 复制代码
vector<int> b = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

// all_of 遇到第一个 false 就停止
// 这里检查到 1 时就知道不是全偶数,直接返回 false
all_of(b.begin(), b.end(), [](int x) {
    return x % 2 == 0;
});

// any_of 遇到第一个 true 就停止
// 这里检查到 6 时就知道有大于 5 的,直接返回 true
any_of(b.begin(), b.end(), [](int x) {
    return x > 5;
});

竞赛实用场景

cpp 复制代码
// 判断是否存在负数
bool hasNegative = any_of(a.begin(), a.end(), [](int x) {
    return x < 0;
});

// 判断是否全为正数
bool allPositive = none_of(a.begin(), a.end(), [](int x) {
    return x <= 0;
});

// 判断是否全为 0
bool allZero = all_of(a.begin(), a.end(), [](int x) {
    return x == 0;
});

五、Lambda 捕获列表详解

1. 捕获方式

捕获方式 说明 示例
[] 不捕获任何变量 [] { return 1; }
[&] 引用捕获所有外部变量 [&] { return x + y; }
[=] 值捕获所有外部变量 [=] { return x + y; }
[x] 值捕获变量 x [x] { return x * 2; }
[&x] 引用捕获变量 x [&x] { x++; }
[=, &x] 值捕获所有,但 x 引用捕获 [=, &x] { x++; }

2. 竞赛实用示例

cpp 复制代码
int n = 10;
vector<int> a(n);

// 值捕获 n
sort(a.begin(), a.end(), [=](int x, int y) {
    return x < y;
});

// 引用捕获 a(可修改)
for_each(a.begin(), a.end(), [&](int &x) {
    x *= 2;
});

六、竞赛实战案例

1. 自定义排序 + 贪心

cpp 复制代码
// 活动选择问题
struct Activity {
    int start, end, id;
};

int main() {
    int n;
    cin >> n;
    vector<Activity> acts(n);
    for (int i = 0; i < n; i++) {
        cin >> acts[i].start >> acts[i].end;
        acts[i].id = i + 1;
    }
    
    // 按结束时间升序排序
    sort(acts.begin(), acts.end(), [](const Activity& a, const Activity& b) {
        return a.end < b.end;
    });
    
    int cnt = 0, lastEnd = -1;
    for (auto& act : acts) {
        if (act.start >= lastEnd) {
            cnt++;
            lastEnd = act.end;
        }
    }
    
    cout << cnt << "\n";
    return 0;
}

2. 离散化 + 排序

cpp 复制代码
vector<int> a = {5, 2, 8, 1, 9, 3};
vector<int> vals = a;

// 排序 + 去重
sort(vals.begin(), vals.end());
vals.erase(unique(vals.begin(), vals.end()), vals.end());

// 离散化
for (int &x : a) {
    x = lower_bound(vals.begin(), vals.end(), x) - vals.begin() + 1;
}

3. 自定义哈希函数(unordered_map)

cpp 复制代码
struct PairHash {
    size_t operator()(const pair<int, int>& p) const {
        return hash<int>()(p.first) ^ hash<int>()(p.second);
    }
};

unordered_map<pair<int, int>, int, PairHash> mp;
mp[{1, 2}] = 10;

七、Lambda 核心要点总结

要点 说明
sort 比较 return true 表示 a 排在 b 前面
priority_queue 比较 return true 表示 a 优先级低于 b(沉底)
捕获列表 [&] 引用捕获,[=] 值捕获,竞赛中常用 [&]
auto 推导 返回类型可省略,编译器自动推导
性能 Lambda 与普通函数性能相同,编译器会内联优化

记忆口诀

  • sorttrue = 排前面
  • priority_queuetrue = 沉底(优先级低)
  • 小根堆a > b
  • 大根堆a < b

八、完整模板代码(竞赛可直接复制)

cpp 复制代码
#include <iostream>
#include <vector>
#include <algorithm>
#include <queue>
#include <functional>

using namespace std;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    // === sort 示例 ===
    vector<int> a = {5, 2, 8, 1, 9, 3};
    
    // 降序
    sort(a.begin(), a.end(), [](int x, int y) {
        return x > y;
    });
    
    // === priority_queue 示例 ===
    // 小根堆
    priority_queue<int, vector<int>, greater<int>> minHeap;
    
    // 自定义比较
    auto cmp = [](const pair<int, int>& a, const pair<int, int>& b) {
        return a.first > b.first;  // 小根堆
    };
    priority_queue<pair<int, int>, vector<pair<int, int>>, decltype(cmp)> pq(cmp);
    
    // === STL 算法示例 ===
    vector<int> b = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    
    // 统计偶数
    int cnt = count_if(b.begin(), b.end(), [](int x) {
        return x % 2 == 0;
    });
    
    return 0;
}

九、常见错误与注意事项

1. priority_queue 比较函数写反

cpp 复制代码
// 错误:这是大根堆
auto cmp = [](int a, int b) {
    return a < b;  // 错误!priority_queue 中 true 表示优先级低
};

// 正确:小根堆
auto cmp = [](int a, int b) {
    return a > b;  // 正确!
};

2. 捕获列表使用不当

cpp 复制代码
int n = 10;

// 错误:值捕获无法修改 n
auto f1 = [=]() {
    n++;  // 编译错误
};

// 正确:引用捕获可以修改
auto f2 = [&]() {
    n++;  // 正确
};

3. Lambda 作为模板参数

cpp 复制代码
// 错误:不能直接作为模板参数
priority_queue<int, vector<int>, [](int a, int b) { return a > b; }> pq;

// 正确:使用 decltype
auto cmp = [](int a, int b) { return a > b; };
priority_queue<int, vector<int>, decltype(cmp)> pq(cmp);

总结 :Lambda 表达式是竞赛中最常用的特性之一,尤其在自定义排序、优先队列、STL 算法中不可或缺。掌握 Lambda 可以大幅简化代码,提高编写效率。

相关推荐
稚南城才子,乌衣巷风流1 小时前
支配树(Dominator Tree)详解:概念、算法与应用
算法
海天鹰2 小时前
PHP上传文件
android·开发语言·php
稚南城才子,乌衣巷风流2 小时前
动态开点:原理、实现与应用场景
数据结构·算法
yyds_yyd_100862 小时前
1464. 数组中两元素的最大乘积(2026.07.27)
数据结构·c++·算法·leetcode
腻害兔2 小时前
【若依项目-产品经理视角】RuoYi-Vue-Pro 源码拆解:IM 即时通讯模块,一个被低估的「全功能聊天系统」
java·前端·vue.js·产品经理·ai编程
心念枕惊3 小时前
.NET CORE 授权进阶-角色、策略与动态权限实现
java·前端·.netcore
KaMeidebaby3 小时前
卡梅德生物技术快报 | 核酸适配体文库测序:核酸适配体文库测序的技术原理、实验流程与数据解析
前端·网络·数据库·人工智能·算法
Yeauty3 小时前
渲染成图再 CLI 拼接,还是进程内直推?Rust 帧到视频的两条路
开发语言·rust·音视频
geovindu4 小时前
CSharp: Breadth First Search Algorithm and Depth First Search Algorithm
开发语言·后端·算法·c#·.net·搜索算法