1. 为什么需要自定义vector排序在日常开发中我们经常遇到标准排序规则无法满足需求的情况。比如处理二维坐标点时可能需要先按x轴降序排列x相同的再按y轴升序排列或者处理任务队列时需要根据任务优先级和创建时间进行多级排序。这时候就需要自定义排序规则。C的vector容器虽然提供了默认的sort排序但面对复杂数据结构时往往力不从心。我最近在优化一个游戏引擎的渲染系统时就遇到了需要对十万级别的精灵图元按层级和深度排序的需求默认排序完全无法满足性能要求。2. 基础排序方法比较函数2.1 一维vector的排序我们先从最简单的例子开始。假设有一个整数vectorvectorint numbers {3,1,4,1,5,9,2,6};默认的升序排序很简单sort(numbers.begin(), numbers.end()); // 结果1,1,2,3,4,5,6,9如果想降序排列可以使用标准库提供的greatersort(numbers.begin(), numbers.end(), greaterint()); // 结果9,6,5,4,3,2,1,12.2 二维vector的排序当处理二维vector时情况就变得有趣了。考虑一个存储坐标点的vectorvectorvectorint 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 vectorint a, const vectorint 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再比较secondvectorpairint,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 pairint,int a, const pairint,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基础用法C11引入的Lambda表达式让自定义排序变得更简洁。上面的例子可以改写为sort(points.begin(), points.end(), [](const vectorint a, const vectorint b) { if(a[0] b[0]) return a[1] b[1]; return a[0] b[0]; });Lambda的语法糖让我们可以就地定义排序规则不需要额外写比较函数。这在算法竞赛和快速原型开发中特别有用。4.2 使用auto简化代码C14进一步增强了Lambda的能力允许参数使用autosort(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_mapint, 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而不是sortvectorStudent 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; });这种模式清晰表达了先按部门升序同部门按薪资降序最后按入职时间升序的业务逻辑。我在处理人力资源系统时就用这种方式实现了复杂的员工排序需求。