当前位置: 首页 > news >正文

C++ vector 自定义排序实战:从基础规则到Lambda表达式进阶

1. 为什么需要自定义vector排序?

在日常开发中,我们经常遇到标准排序规则无法满足需求的情况。比如处理二维坐标点时,可能需要先按x轴降序排列,x相同的再按y轴升序排列;或者处理任务队列时,需要根据任务优先级和创建时间进行多级排序。这时候就需要自定义排序规则。

C++的vector容器虽然提供了默认的sort排序,但面对复杂数据结构时往往力不从心。我最近在优化一个游戏引擎的渲染系统时,就遇到了需要对十万级别的精灵图元按层级和深度排序的需求,默认排序完全无法满足性能要求。

2. 基础排序方法:比较函数

2.1 一维vector的排序

我们先从最简单的例子开始。假设有一个整数vector:

vector<int> numbers = {3,1,4,1,5,9,2,6};

默认的升序排序很简单:

sort(numbers.begin(), numbers.end()); // 结果:1,1,2,3,4,5,6,9

如果想降序排列,可以使用标准库提供的greater:

sort(numbers.begin(), numbers.end(), greater<int>()); // 结果:9,6,5,4,3,2,1,1

2.2 二维vector的排序

当处理二维vector时,情况就变得有趣了。考虑一个存储坐标点的vector:

vector<vector<int>> points = {{0,2},{1,5},{1,9},{4,6},{5,9},{8,10}};

默认排序会先比较每个子vector的第一个元素,相等时再比较第二个:

sort(points.begin(), points.end()); // 结果:{0,2},{1,5},{1,9},{4,6},{5,9},{8,10}

但如果我们想要先按x坐标降序,x相同再按y升序呢?这就需要自定义比较函数:

static bool cmp(const vector<int>& a, const vector<int>& b) { if(a[0] == b[0]) return a[1] < b[1]; // x相同则y升序 return a[0] > b[0]; // x降序 } sort(points.begin(), points.end(), cmp); // 结果:{8,10},{5,9},{4,6},{1,5},{1,9},{0,2}

这里有个关键点:比较函数必须声明为static。这是因为在类内部定义时,非静态成员函数有隐含的this指针参数,与sort函数期望的调用约定不匹配。我在LeetCode刷题时就因为这个坑卡了半天。

3. pair类型的排序技巧

3.1 pair的基本排序

pair是C++中非常实用的模板类,常用于存储键值对。它的默认排序规则是先比较first,再比较second:

vector<pair<int,int>> pairs = {{0,2},{1,5},{1,9},{4,6},{5,9},{8,10}}; sort(pairs.begin(), pairs.end()); // 结果:{0,2},{1,5},{1,9},{4,6},{5,9},{8,10}

3.2 自定义pair排序

假设我们要实现和之前一样的排序规则:first降序,second升序。比较函数可以这样写:

static bool cmp(const pair<int,int>& a, const pair<int,int>& b) { if(a.first == b.first) return a.second < b.second; return a.first > b.first; } sort(pairs.begin(), pairs.end(), cmp); // 结果:{8,10},{5,9},{4,6},{1,5},{1,9},{0,2}

在实际项目中,我经常用pair来存储带权重的数据。比如在实现Dijkstra算法时,就需要按距离优先处理节点,这时候自定义排序就派上用场了。

4. Lambda表达式:更现代的解决方案

4.1 Lambda基础用法

C++11引入的Lambda表达式让自定义排序变得更简洁。上面的例子可以改写为:

sort(points.begin(), points.end(), [](const vector<int>& a, const vector<int>& b) { if(a[0] == b[0]) return a[1] < b[1]; return a[0] > b[0]; });

Lambda的语法糖让我们可以就地定义排序规则,不需要额外写比较函数。这在算法竞赛和快速原型开发中特别有用。

4.2 使用auto简化代码

C++14进一步增强了Lambda的能力,允许参数使用auto:

sort(points.begin(), points.end(), [](const auto& a, const auto& b) { if(a[0] == b[0]) return a[1] < b[1]; return a[0] > b[0]; });

这种写法更通用,可以适配不同类型的容器元素。我在处理模板代码时特别喜欢用这种方式,可以减少很多重复代码。

4.3 捕获外部变量

Lambda的强大之处在于它能捕获外部变量。比如我们需要根据一个外部定义的权重表来排序:

unordered_map<int, int> weight_table = {{0,5}, {1,3}, {4,8}, {5,2}, {8,1}}; sort(points.begin(), points.end(), [&weight_table](const auto& a, const auto& b) { return weight_table[a[0]] < weight_table[b[0]]; });

这个特性在处理复杂业务逻辑时非常有用。记得我在开发一个推荐系统时,就利用这个特性实现了基于多种因素加权的动态排序。

5. 性能考量与最佳实践

5.1 比较函数与Lambda的性能

很多人担心Lambda会带来性能开销,但实际上现代编译器对简单Lambda的优化非常好。在我的测试中,对于基本类型的排序,Lambda和普通函数的性能差异可以忽略不计。

但对于复杂对象,建议尽量使用引用传递参数,避免不必要的拷贝:

// 好:使用const引用 sort(items.begin(), items.end(), [](const BigObject& a, const BigObject& b) { return a.value < b.value; }); // 不好:值传递会导致拷贝 sort(items.begin(), items.end(), [](BigObject a, BigObject b) { return a.value < b.value; });

5.2 稳定排序的重要性

当排序的等价元素需要保持原有相对顺序时,应该使用stable_sort而不是sort:

vector<Student> students = {...}; stable_sort(students.begin(), students.end(), [](const auto& a, const auto& b) { return a.grade > b.grade; // 按成绩降序,同分学生保持原顺序 });

在处理GUI元素排序或者有多个排序条件的场景下,这个特性特别有用。

5.3 多条件排序的技巧

当需要按多个条件排序时,可以这样组织代码:

sort(employees.begin(), employees.end(), [](const auto& a, const auto& b) { if(a.department != b.department) return a.department < b.department; if(a.salary != b.salary) return a.salary > b.salary; return a.hire_date < b.hire_date; });

这种模式清晰表达了"先按部门升序,同部门按薪资降序,最后按入职时间升序"的业务逻辑。我在处理人力资源系统时,就用这种方式实现了复杂的员工排序需求。

http://www.jsqmd.com/news/682924/

相关文章:

  • MySQL运维实战:5.7.26版本服务异常启动排查与修复
  • 2026年工商注册服务机构推荐:河南紫萄财务咨询服务有限公司,提供内黄、台前、鹤壁等多地工商注册服务 - 品牌推荐官
  • C#调用Llama-3-8B本地推理实测:.NET 11 Zero-Copy Tensor Binding技术首度公开(含完整Benchmark数据)
  • Xray实战:如何像渗透测试老手一样配置HTTP代理模式抓取敏感接口
  • Jmeter性能测试踩坑记:我的Token为什么在第二个线程组里失效了?
  • RDP Wrapper Library:解锁Windows远程桌面多用户连接的终极方案
  • 2026年研发/实验室用/半导体/高精度CMP抛光设备哪家好?品牌厂家推荐:北京华沛智同 - 品牌推荐大师
  • 2026年超声波探头片/传感器片厂家推荐:陕西久源传感电子科技有限公司,全系列传感片稳定供应 - 品牌推荐官
  • 告别ifconfig依赖:在SUSE15上我更推荐你用‘ip’命令,附完整新旧命令对照表
  • Qianfan-OCR开源部署教程:4B多模态模型一键启动实战
  • Phi-3.5-mini-instructGPU算力:消费级显卡跑专业级多语言模型
  • OpenCV solvePnP实战:从原理到三维距离计算的完整指南
  • 2026年舞台设计搭建及展会搭建服务推荐:佛山市轩庆庆典礼仪有限公司,专业服务商务、庆典、展会等多元活动 - 品牌推荐官
  • 从地理数据到商业洞察:手把手教你用SPSS 27搞定10种数据分析(附实战数据集)
  • 中小制造企业数字化转型避坑指南:PLM、ERP、MES、CRM该怎么选和分步上?
  • 广东顺业钢材:性价比高的东莞螺纹钢切割定尺设备 - LYL仔仔
  • PostgreSQL pg_dump对象名称中有换行符时可导致psql客户端及恢复目标服务器执行任意恶意代码HGVE-2025-E008
  • 当ARM CPU彻底挂死,别慌!手把手教你用DS-5的CSAT命令行工具抢救内存数据
  • B站视频下载终极指南:用BilibiliDown轻松保存喜欢的视频内容 [特殊字符]
  • 2026快速申请香港大学研究生,靠谱留学机构推荐 - 品牌2026
  • flutter开源项目
  • Qwen3-4B-Thinking应用案例:如何用它快速生成营销文案和编程代码?
  • 掌握高效视频下载:BilibiliDown跨平台B站视频下载器完全指南
  • Phi-3.5-mini-instruct效果对比:相同温度下,中文回答连贯性 vs 英文回答质量差异分析
  • 裸机环境下运行Phi-3-mini的完整移植手记(无RTOS、无malloc、仅128KB RAM)——含GCC链接脚本定制与中断向量重映射详解
  • 2026年空调回收厂家推荐:郑州怀强回收,模块机/一拖多/三匹/商用/写字楼/多联机等全品类空调回收 - 品牌推荐官
  • 明日方舟游戏素材完整指南:如何快速获取并使用官方美术资源
  • GitHub 6.6k 星!让 Claude 瞬间读懂整个代码库的神器
  • 免费论文降重降AI工具盘点:10款实用工具+SpeedAI使用指南
  • Qianfan-OCR一文详解:InternViT视觉编码器对复杂版式文档的建模优势