c++ map是有序还是无序的_map的默认排序和自定义排序

博客介绍了STL容器map,因其内部红黑树结构,查找复杂度为O(log2N),默认按key升序排序。还说明了对map进行特殊排序的方法,包括基本类型key降序排列和自定义类型key排序,展示了不同排序的代码示例,体现了map使用的自由度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

STL的容器map为我们处理有序key-value形式数据提供了非常大的便利,由于内部红黑树结构的存储,查找的时间复杂度为O(log2N)。

一般而言,使用map的时候直接采取map的形式即可,map的内部实现默认使用A类型变量的升序来排序map的值。

但是有时我们需要对map的值做特殊的排序(不经其他容器的辅助),这就需要在定义map变量时做些特殊的处理。

STL中map的定义是:

1 template,4 class _Alloc = allocator>>

5 classmap6 : public _Tree<_tmap_traits _ty _pr _alloc false>>

7 {

这是一个模板类,我们的特殊处理主要改造的就是class _Pr = less<_kty>,并且从这里我们也能看到,无论做哪种修改,排序都是针对key而言的,要实现value的自定义排序,

不是修改_Pr类型能完成的。

替换_Pr的也必须是一个类型,即至少我们要自己创建一个类型,用来做key的比较。自然,我们需要做的是重载函数调用操作符"()",一般的形式为

1 classT{2 public:3 bool operator()(const T& lhs, const T& rhs)const

4 {5 ...6 }7 };

代码需包含头文件、。

下面是常见的一些自定义排序:

a.对基本类型的key以降序排列

map默认提供的是less<_kty>类型的排序方式,阅读STL源码

1 template

2 structless3 { //functor for operator<

4 _CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef _Ty first_argument_type;5 _CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef _Ty second_argument_type;6 _CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef boolresult_type;7

8 constexpr bool operator()(const _Ty& _Left, const _Ty& _Right) const

9 { //apply operator< to operands

10 return (_Left <_right>

修改上述代码的第10行,为修改后的类型起一个自定义名字很简单,不过STL已经为我们提供了整个类型定义:

1 template

2 structgreater3 { //functor for operator>

4 _CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef _Ty first_argument_type;5 _CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef _Ty second_argument_type;6 _CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef boolresult_type;7

8 constexpr bool operator()(const _Ty& _Left, const _Ty& _Right) const

9 { //apply operator> to operands

10 return (_Left >_Right);11 }12 };

我们直接使用就行:

1 std::map>mi;2 for (int i = 0; i < 5; i++)3 {4 mi[i] = i * 2;5 }6

7 std::for_each(mi.begin(), mi.end(),8 [](const std::map>::value_type&vl) {9 cout << "key:" << vl.first << "  value:" << vl.second << '\n';10 });

对应的输出为:

这里,我们实现了按key降序排列的目的。

b.为自定义类型的key做排序:

自定义类型的key定义map时(使用map默认排序),我们一般都要做一件事:为自定义类型重载“

因此,我们替换less<_kty>也同样是要做这样的事:自定义排序规则,比如:

1 classMyKey {2 public:3 MyKey(int fidx = 0, int sidx = 0)4 :m_firstIdx(fidx), m_secondIdx(sidx) {}5

6 intm_firstIdx;7 intm_secondIdx;8 };9

10 classMyCompare{11 public:12 bool operator()(const MyKey& lhs, const MyKey& rhs)const

13 {14 if (lhs.m_firstIdx >rhs.m_firstIdx)15 {16 return true;17 }18 else if (lhs.m_firstIdx ==rhs.m_firstIdx)19 {20 return lhs.m_secondIdx >rhs.m_secondIdx;21 }22 return false;23 }24 };25

26 classMyCompare2 {27 public:28 bool operator()(const MyKey& lhs, const MyKey& rhs)const

29 {30 return lhs.m_firstIdx >rhs.m_firstIdx;31 }32 };

使用MyCompare:

1 std::mapmi;2 for (int i = 0; i < 5; i++)3 {4 mi[MyKey(i * 2, i)] = i * 2;5 }6

7 std::for_each(mi.begin(), mi.end(),8 [](const std::map::value_type&vl) {9 cout << "key:" << vl.first.m_firstIdx << "-" << vl.first.m_secondIdx << "value:" << vl.second << '\n';10 });

使用MyCompare2:

1 std::mapmi;2 for (int i = 0; i < 5; i++)3 {4 mi[MyKey(i * 2, i)] = i * 2;5 }6

7 std::for_each(mi.begin(), mi.end(),8 [](const std::map::value_type&vl) {9 cout << "key:" << vl.first.m_firstIdx << "-" << vl.first.m_secondIdx << "value:" << vl.second << '\n';10 });

以上两种有相同的输出:

我们实现了自定义类型自定义排序的目的。

可以看到,使用map其实有很大的自由度,我们完全可以定制自己的map,为我们解决问题、精简代码带来很大的便利。

<think>好的,用户现在问的是如何在C++map自定义排序。之前他问过关于vector<pair<string, double>>如何根据键查找数据的问题,我给出了线性查找二分查找的方法,并建议改用map或unordered_map以提高效率。现在他可能已经决定使用map,但需要根据特定需求自定义排序规则,所以需要指导如何实现这一点。 首先,我需要回忆C++map默认排序行为。默认情况下,map使用std::less<Key>来排序键,对于字符串来说是按字典序升序排列。用户可能需要降序或者其他自定义排序方式,比如不区分大小写,或者按照字符串长度排序。 接下来,我需要考虑如何自定义排序。这通常涉及到在声明map时指定第三个模板参数,即比较函数(仿函数)。比较函数需要是一个严格弱序,确保正确的排序行为。 然后,我需要考虑用户可能的场景。比如用户可能希望按字符串长度排序,或者忽略大小写进行比较。需要提供这两种常见情况的示例代码,并解释注意事项,比如比较函数必须满足严格弱序,否则会导致未定义行为。 同时,需要注意比较函数在作为模板参数时,需要是类型而非对象,所以通常使用结构体或类,并重载operator()。如果是使用lambda,可能需要在C++20或更高版本中使用decltype构造函数参数,但这可能复杂,所以可能还是建议使用结构体更通用。 另外,用户可能没有意识到使用自定义排序时,查找操作也会受到影响,比如如果使用字符串长度排序,查找时也需要用相同长度的键,否则可能找不到。需要提醒用户注意这一点。 还需要提到关联容器的特性,比如map的键是唯一的,插入查找的时间复杂度是O(log n)。同时,如果用户需要更复杂的排序规则,可能需要调整比较函数的实现。 最后,应该对比使用自定义排序mapunordered_map的区别,指出当需要特定排序时使用map,而无需排序但需要快速查找时使用unordered_map。同时,提醒用户自定义排序可能带来的性能影响,比如字符串长度比较可能比直接字典序慢一些。 总结回答结构:首先介绍默认排序,然后如何自定义,提供两种示例(降序字符串长度),强调比较函数的注意事项,最后给出建议常见问题解答。</think>在C++中,`std::map`默认按键的**升序**排列(基于`std::less<Key>`),但可以通过自定义比较函数实现任意排序规则。以下是具体方法: --- ### 核心原理 通过模板的第三个参数指定**比较函数类型**: ```cpp template< class Key, class T, class Compare = std::less<Key> // 默认比较函数 > class map; ``` 自定义时需要: 1. 定义比较函数(仿函数或lambda) 2. 满足**严格弱序**(Strict Weak Ordering) --- ### 方法1:使用仿函数(推荐) #### 示例1:按字符串**降序**排列 ```cpp #include <map> #include <string> // 自定义比较规则:字符串降序 struct CompareDesc { bool operator()(const std::string& a, const std::string& b) const { return a > b; // 注意此处为 ">" } }; int main() { // 模板参数3传入仿函数类型 std::map<std::string, int, CompareDesc> myMap; myMap["apple"] = 5; myMap["banana"] = 3; myMap["cherry"] = 7; // 输出顺序:cherry -> banana -> apple return 0; } ``` #### 示例2:按字符串**长度**排序 ```cpp struct CompareLength { bool operator()(const std::string& a, const std::string& b) const { if (a.length() == b.length()) { return a < b; // 长度相同时按字典序升序 } return a.length() < b.length(); // 短字符串在前 } }; int main() { std::map<std::string, int, CompareLength> lenMap; lenMap["z"] = 1; lenMap["apple"] = 2; lenMap["kiwi"] = 3; // 输出顺序:z -> kiwi -> apple return 0; } ``` --- ### 方法2:使用Lambda(C++20+) ```cpp auto comp = [](const std::string& a, const std::string& b) { return a > b; // 降序规则 }; // 需要传递Lambda的类型(通过decltype) std::map<std::string, int, decltype(comp)> customMap(comp); ``` --- ### 注意事项 1. **严格弱序**必须满足: - 若`comp(a,b)==true`,则`comp(b,a)==false`(反对称性) - 若`comp(a,b)==true`且`comp(b,c)==true`,则`comp(a,c)==true`(传递性) - `comp(a,a)==false`(非自反性) 2. **查找行为受排序规则影响**: - 若按长度排序,查找时需用相同长度的键 - 例如:`lenMap.find("grape")`会失败("grape"长度为5,但`lenMap`中不存在长度5的键) --- ### 对比其他容器 | 容器 | 排序方式 | 适用场景 | |-----------------|---------------|---------------------------| | `std::map` | 自定义排序 | 需要有序遍历/范围查询 | | `std::unordered_map` | 哈希无序 | 仅需快速查找($O(1)$平均) | --- ### 常见问题 **Q:如何实现不区分大小写的排序?** ```cpp struct CaseInsensitiveCompare { bool operator()(const std::string& a, const std::string& b) const { return std::lexicographical_compare( a.begin(), a.end(), b.begin(), b.end(), [](char c1, char c2) { return tolower(c1) < tolower(c2); } ); } }; ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值