当前位置: 首页 > 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.wxhsa.cn/company.asp?id=6043

相关文章:

  • id
  • 【汇总】Qt常用模块头文件
  • Advanced Algorithm —— Hashing and Sketching
  • CF2136 Codeforces Round 1046 (Div. 2) 补题
  • 【IEEE出版、EI检索稳定】第四届云计算、大数据应用与软件工程国际学术会议(CBASE 2025)
  • 缺省源
  • 97. 交错字符串
  • MODint(自动取模)
  • BFD实验
  • 2025.9.16——卷1阅读程序1、2
  • 用Context Offloading解决AI Agent上下文污染,提升推理准确性
  • HCIP-BFD
  • MISC相关
  • VRRP实验
  • 在 Windows 10 上安装 FFmpeg 8.0
  • 25/9/15(补)
  • [Paper Reading] DINOv3
  • 25/9/16
  • JavaDay5
  • 揭秘Mobile Me数据挖掘:从WebDAV探测到隐藏文件发现
  • 25/9/14(补)
  • 【IEEE出版、往届会后4个月EI检索】第二届计算机视觉、图像处理与计算摄影国际学术会议(CVIP 2025)
  • 洛谷 P10936 导弹防御塔 题解
  • P13694 [CEOI 2025] Splits 题解
  • VSCode + Python 开发踩坑:虚拟环境不在项目根目录导致包无法识别该怎么办
  • 图像与视频编码
  • Python爬虫实战:研究Pandas,构建地理信息资料采集和分析便捷的系统
  • 初赛复习
  • 用户帐户控制(UAC)
  • fg/bg/jobs/kill命令--linux