C++: 根据key或value对map排序

本文介绍了C++ STL中Map容器的排序方法,包括默认排序和自定义排序方式,并提供了根据key长度排序的具体实例,同时讲解了如何将Map转换为Vector进行value排序。

C++: 根据key或value对map排序

June 06, 2015

map是c++ STL里built-in的数据结构,其元素为pair,pair.first为map的key,pair.second为map的value。
由于map是集合容器,而非序列容器(像vector,list,queue等),所以我们并不能直接用stl里的sort对map进行排序。
下面我们分别讨论根据key和value对相应的map排序。


根据map的key排序
根据key的大小默认排序

map默认是按照的key的大小进行排序的,所以作为map的key必须能够进行<比较。示例说明:

#include <iostream>
#include <map>
#include <string>
using namespace std;

void printMap(map<string,int> &m){
    map<string, int>::iterator it;
    for(it  = m.begin(); it != m.end(); ++it){
        cout<<it->first<<" : "<<it->second<<endl;
    }
}

int main(){
    map<string,int> m;
    m["Hello"] = 100;
    m["World"] = 34;
    m["C++"]   = 12;
    m.insert(pair<string,int>("Java",23));
    m.insert(make_pair("Python",99));
    printMap(m);
    return 0;
}

输出结果为:

     C++ : 12
     Hello : 100
     Java : 23
     Python : 99
     World : 34
自定义key排序方式

默认是按照key由小到大排的序,我们如何自定义排序方式呢?
先看map在stl里面的定义:

template < class Key, class T, class Compare = less<Key>,  
           class Allocator = allocator<pair<const Key,T> > > class map;  

前两个为key,value,第三个称之为函数对象。

所谓的函数对象:即调用操作符的类,其对象常称为函数对象(function object),它们是行为类似函数的对象。表现出一个函数的特征,就是通过“对象名+(参数列表)”的方式使用一个类,其实质是对operator()操作符的重载。

默认使用的是less函数对象,对string而言则是按照字典序排列。当然我们也可以使用另外一个函数对象greater。示例如下:

#include <iostream>
#include <map>
#include <string>
using namespace std;
void printMap(map<string,int, greater<string> > &m){
    map<string, int>::iterator it;
    for(it  = m.begin(); it != m.end(); ++it){
        cout<<it->first<<" : "<<it->second<<endl;
    }
}
int main(){
    //use function object: greater
    map<string,int, greater<string> > m;
    m["Hello"] = 100;
    m["World"] = 34;
    m["C++"]   = 12;
    m.insert(pair<string,int>("Java",23));
    m.insert(make_pair("Python",99));
    printMap(m);
    return 0;
}

输出结果为:

    World : 34
    Python : 99
    Java : 23
    Hello : 100
    C++ : 12    

当然我们也可以自定义一个函数对象,用以构建我们需要排序的map。例如根据key的长度排序。

#include <iostream>
#include <map>
#include <string>
using namespace std;
//build function object
struct compByLen{
    bool operator()(const string &s1, const string &s2){
        return s1.length() < s2.length();
    }
};
void printMap(map<string,int,compByLen> &m){
    map<string, int>::iterator it;
    for(it  = m.begin(); it != m.end(); ++it){
        cout<<it->first<<" : "<<it->second<<endl;
    }
}
int main(){
    //use function object: greater
    map<string,int, compByLen> m;
    m["Hello"] = 100;
    m["World"] = 34;
    m["C++"]   = 12;
    m.insert(pair<string,int>("Java",23));
    m.insert(make_pair("Python",99));
    printMap(m);
    return 0;
}

有bug,待修改


根据value排序

我们不能用sort来最map直接排序,但我们可以把map序列化,放到vector,然后进行排序。sort需要制定range的初始结束位置,以及比较方法。具体见示例:

#include <iostream>
#include <map>
#include <string>
#include <vector>
using namespace std;


void printVec(vector< pair<string,int> > &vec){
    vector<pair<string,int> >::iterator it;
    for(it = vec.begin(); it!= vec.end(); ++it){
        cout<<it->first<<" : "<<it->second<<endl;
    }
}
bool comp_by_value(pair<string,int> &p1, pair<string,int> &p2){
    return p1.second > p2.second;
}
struct CompByValue{
    bool operator()(pair<string,int> &p1, pair<string,int> &p2){
        return p1.second > p2.second;
    }
};
int main(){
    map<string,int> m;
    m["Hello"] = 100;
    m["World"] = 34;
    m["C++"]   = 12;
    m.insert(pair<string,int>("Java",23));
    m.insert(make_pair("Python",99));
    //change to a vector
    vector<pair<string,int> > vec (m.begin(), m.end());
    /*
    map<string, int>::iterator it;
    vector<pair<string,int> > vec;
    for(it = m.begin(); it != m.end(); ++it){
        vec.push_back(*it);
    }
    */
    //use compare function
    sort(vec.begin(),vec.end(),comp_by_value);
    //use funcion object
    //sort(vec.begin(),vec.end(),CompByValue());
    printVec(vec);
    return 0;
}

sort的比较方法,可以利用提供比较函数comp_by_value或者构建函数对象来实现。二者均可。
关于sortstd::sort
参考:C++ STL中Map的按Key排序和按Value排序

<think> 我们正在讨论C++map的使用,特别是根据key查询value的方法。 在C++标准库中,map是一种关联容器,存储键值对(key-value pairs),并按照key进行排序根据key查询value有几种常用方法: 1. 使用operator[]:可以通过下标操作符直接访问,但如果key不存在,会插入一个具有该key的新元素(value为默认构造)。 2. 使用at()成员函数:如果key存在,返回对应的value;如果key不存在,抛出std::out_of_range异常。 3. 使用find()成员函数:返回一个迭代器,指向找到的元素(如果找到),否则返回end()迭代器。 4. 使用count()成员函数:返回keymap中出现的次数(对于map只能是01),可以用来检查是否存在,但不直接返回value。 下面我们详细说明这些方法。 </think> ### C++ 中使用 `map` 根据 key 查询 value 的方法详解 在 C++ 中,`std::map` 是基于红黑树实现的有序关联容器,存储键值对(key-value pairs)。以下是 4 种核心查询方法及其适用场景: #### 1. **下标运算符 `[]`** ```cpp #include <map> std::map<std::string, int> ages = {{"Alice", 30}, {"Bob", 25}}; int aliceAge = ages["Alice"]; // 返回 30 int charlieAge = ages["Charlie"]; // 自动插入 {"Charlie", 0} ``` - ✅ **优点**:简洁直观 - ❌ **缺点**:若 key 不存在会**自动插入**新元素(value 默认初始化) - ⚠️ **注意**:非 const 操作,可能改变 map 结构 #### 2. **`at()` 成员函数**(C++11 起) ```cpp try { int bobAge = ages.at("Bob"); // 返回 25 int eveAge = ages.at("Eve"); // 抛出 std::out_of_range } catch (const std::out_of_range& e) { // 处理 key 不存在的情况 } ``` - ✅ **优点**:提供安全检查,key 不存在时抛出异常 - ❌ **缺点**:异常处理增加开销 - 💡 **适用**:需严格检查 key 存在性的场景 #### 3. **`find()` 迭代器查询** ```cpp auto it = ages.find("Alice"); if (it != ages.end()) { int age = it->second; // 安全访问 value } else { // key 不存在 } ``` - ✅ **优点**:不修改 map,安全高效 - 🚀 **性能**:时间复杂度 $O(\log n)$ - 🌟 **最佳实践**:最推荐的标准查询方式 #### 4. **`count()` 存在性检查** ```cpp if (ages.count("Bob") > 0) { // key 存在(map 中 count 返回 0 1) } ``` - ✅ **优点**:仅检查存在性,不创建新元素 - ❌ **缺点**:无法直接获取 value - 🛠️ **搭配使用**:常与 `find()` 配合避免重复查找 --- ### 方法对比总结 | 方法 | 是否修改 map | key 不存在时的行为 | 性能 | |---------------|--------------|------------------------|------------| | `operator[]` | ✅ 可能插入 | 创建默认值元素 | $O(\log n)$ | | `at()` | ❌ | 抛出异常 | $O(\log n)$ | | `find()` | ❌ | 返回 `end()` 迭代器 | $O(\log n)$ | | `count()` | ❌ | 返回 0 | $O(\log n)$ | --- ### 使用示例:文件类型检测 ```cpp #include <iostream> #include <map> int main() { std::map<std::string, std::string> mimeTypes = { {".txt", "text/plain"}, {".jpg", "image/jpeg"}, {".mp4", "video/mp4"} }; std::string file = "photo.jpg"; std::string ext = file.substr(file.find_last_of('.')); // 提取 ".jpg" // 安全查询 auto it = mimeTypes.find(ext); if (it != mimeTypes.end()) { std::cout << "MIME类型: " << it->second; // 输出: image/jpeg } else { std::cout << "未知文件类型"; } } ``` > **关键提示**:优先使用 `find()` 避免副作用,需修改值时再用 `operator[]`。对于 C++17+,可利用结构化绑定简化代码: > ```cpp > if (auto [it, inserted] = mimeTypes.try_emplace(".png", "image/png"); !inserted) { > std::cout << "Key已存在,值为: " << it->second; > } > ``` --- ### 常见问题解答 **Q: 查询时如何避免不必要的拷贝?** A: 对于大型对象,使用 `find()` + 迭代器访问, C++17 的 `try_emplace()` 避免临时对象构造。 **Q: 是否支持自定义 key 类型?** A: 需为自定义类型提供 `operator<` 自定义比较器: ```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 std::tolower(c1) < std::tolower(c2); }); } }; std::map<std::string, int, CaseInsensitiveCompare> map; map["ALICE"] = 30; std::cout << map.at("alice"); // 输出 30 ``` **Q: 如何提升查询性能?** A: 对于只读场景使用 `std::unordered_map`(哈希表,$O(1)$ 平均复杂度),但需注意: 1. key 需支持 `std::hash` 2. 元素无序存储 3. 最坏复杂度 $O(n)$
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值