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

C++中set与map的自定义排序方法详解

在C++标准模板库(STL)中,set和map是两种常用的关联容器,它们默认按照键的升序进行排序。但在实际开发中,我们经常需要根据特定需求对元素进行自定义排序。本文将详细介绍如何为set和map实现自定义排序。

默认排序行为

在深入了解自定义排序之前,我们先看一下set和map的默认行为:

#include <iostream>
#include <set>
#include <map>int main() {// 默认升序排列的setstd::set<int> defaultSet = {3, 1, 4, 1, 5, 9};for (int num : defaultSet) {std::cout << num << " "; // 输出: 1 3 4 5 9}std::cout << std::endl;// 默认升序排列的mapstd::map<int, std::string> defaultMap = {{3, "three"},{1, "one"},{4, "four"}};for (auto& pair : defaultMap) {std::cout << pair.first << ":" << pair.second << " "; // 输出: 1:one 3:three 4:four}return 0;
}

实现自定义排序

C++提供了两种主要方式来实现自定义排序:

1. 使用函数对象(Functor)

函数对象是一个重载了函数调用操作符(())的类,可以作为比较器传递给set或map。

#include <iostream>
#include <set>
#include <map>// 降序排序的函数对象
struct DescendingOrder {bool operator()(int a, int b) const {return a > b; // 降序排列}
};// 按字符串长度排序的函数对象
struct StringLengthComparator {bool operator()(const std::string& a, const std::string& b) const {return a.length() < b.length(); // 按长度升序}
};int main() {// 使用自定义排序的set(降序)std::set<int, DescendingOrder> customSet = {3, 1, 4, 1, 5, 9};for (int num : customSet) {std::cout << num << " "; // 输出: 9 5 4 3 1}std::cout << std::endl;// 使用自定义排序的map(按字符串长度)std::map<std::string, int, StringLengthComparator> lengthMap;lengthMap["apple"] = 5;lengthMap["banana"] = 6;lengthMap["cherry"] = 6;lengthMap["date"] = 4;for (auto& pair : lengthMap) {std::cout << pair.first << ":" << pair.second << " "; // 输出: date:4 apple:5 banana:6 cherry:6// 注意:长度相同的元素,按字典序排列}return 0;
}

2. 使用Lambda表达式(C++11及以上)

C++11引入了Lambda表达式,可以更简洁地定义比较器。

#include <iostream>
#include <set>
#include <map>
#include <string>int main() {// 使用Lambda表达式定义降序排序auto descendingLambda = [](int a, int b) { return a > b; };std::set<int, decltype(descendingLambda)> lambdaSet(descendingLambda);lambdaSet.insert({3, 1, 4, 1, 5, 9});for (int num : lambdaSet) {std::cout << num << " "; // 输出: 9 5 4 3 1}std::cout << std::endl;// 对于map,使用Lambda按值排序(需要特殊技巧)std::map<std::string, int> normalMap = {{"apple", 5},{"banana", 2},{"cherry", 8},{"date", 3}};// 创建一个按值排序的"视图"auto valueComparator = [&normalMap](const std::string& a, const std::string& b) {return normalMap[a] < normalMap[b];};std::set<std::string, decltype(valueComparator)> valueOrderedSet(valueComparator);for (auto& pair : normalMap) {valueOrderedSet.insert(pair.first);}std::cout << "按值排序: ";for (auto& key : valueOrderedSet) {std::cout << key << ":" << normalMap[key] << " "; // 输出: banana:2 date:3 apple:5 cherry:8}return 0;
}

自定义对象排序

当set或map的元素是自定义对象时,自定义排序尤为重要。

#include <iostream>
#include <set>
#include <string>class Person {
public:std::string name;int age;Person(std::string n, int a) : name(n), age(a) {}
};// 按年龄排序的函数对象
struct AgeComparator {bool operator()(const Person& a, const Person& b) const {return a.age < b.age;}
};// 按姓名排序的函数对象
struct NameComparator {bool operator()(const Person& a, const Person& b) const {return a.name < b.name;}
};int main() {// 按年龄排序的setstd::set<Person, AgeComparator> ageSet;ageSet.insert(Person("Alice", 30));ageSet.insert(Person("Bob", 25));ageSet.insert(Person("Charlie", 35));std::cout << "按年龄排序: ";for (auto& person : ageSet) {std::cout << person.name << "(" << person.age << ") ";}std::cout << std::endl;// 按姓名排序的setstd::set<Person, NameComparator> nameSet;nameSet.insert(Person("Charlie", 35));nameSet.insert(Person("Alice", 30));nameSet.insert(Person("Bob", 25));std::cout << "按姓名排序: ";for (auto& person : nameSet) {std::cout << person.name << "(" << person.age << ") ";}return 0;
}

注意事项

  1. 严格弱序:自定义比较器必须满足严格弱序关系,即:

    • 非自反性:comp(a, a) 必须为false
    • 不对称性:如果comp(a, b)为true,则comp(b, a)必须为false
    • 可传递性:如果comp(a, b)为true且comp(b, c)为true,则comp(a, c)必须为true
  2. 相等判断:在set和map中,如果!comp(a, b) && !comp(b, a),则认为a和b相等(重复元素),不会被插入

  3. 性能考虑:比较器的性能会影响容器操作的效率,应确保比较操作尽可能高效

总结

C++中的set和map提供了灵活的自定义排序机制,可以通过函数对象或Lambda表达式实现。掌握这一特性可以让你更好地控制容器中元素的组织方式,满足各种复杂的业务需求。无论是基本数据类型还是自定义对象,都可以通过实现适当的比较器来定义排序规则。

在实际开发中,选择合适的数据结构和排序方式对程序性能和可维护性至关重要,希望本文能帮助你在使用set和map时做出更明智的选择。

http://www.rkmt.cn/news/6306.html

相关文章:

  • id
  • 缺省源
  • MISC相关
  • 在 Windows 10 上安装 FFmpeg 8.0
  • 揭秘Mobile Me数据挖掘:从WebDAV探测到隐藏文件发现
  • 洛谷 P10936 导弹防御塔 题解
  • 初赛复习
  • 用户帐户控制(UAC)
  • 6款超好用的AI换脸软件,一键视频直播换脸(附下载链接)
  • KUKA程序中DEF 与 DEFFCT 的区别
  • 第一天作业
  • 八皇后问题
  • 2025.9.16日软件工程学习日志
  • 2025ccpc南昌邀请赛感想+补题
  • img标签如何去除边框?
  • 25.9.16 java se大致了解后开始学习MySQL
  • C++ + OpenCV + Tesseract 实现英文数字验证码识别
  • 表格识别技术:“唤醒”沉睡在纸质文档中的海量结构化数据
  • 《软件需求最佳实践》阅读笔记一
  • 计数原理与排列组合
  • 9.16动态用例设计方法 笔记
  • 深入解析:ESP32三种主流的开发环境
  • js
  • 9.16电商状态迁移图
  • 总结-CDQ 分治
  • Win10玩LOL弹窗
  • Week 1 Homework
  • 异地办公文件同步,多台设备如何无缝同步最新教程
  • CSP-S模拟22
  • K8S探针