C++STL之map

std::map是STL中有序的关联容器,它内部存储的元素都是键值对的形式的,其中key是唯一,不可重复,但是value却没有限制,同时std::map中的元素都会按照key值自动排序,因此它是有序容器。

同样对于map的介绍可以参考以下这两个网站:

cplusplus

cppreference

实现原理

std::map底层使用红黑树作为底层的数据结构。红黑树是一种自动平衡的二叉树,它确保插入、删除和查找操作的时间复杂度都是O(log n)。

因为std::map需要排序,因此它是有一定的性能损耗的,如果你仅仅需要一个关联容器,并不关心排序问题,那么建议优先使用unordered_map。 相对于std::map的话std::unordered_map是基于哈希表实现的,具有常数时间复杂度的平均性能。

因为std::map被称为有序容器,所以对插入进去的key有排序的要求。一般需要为类实现<比较方法,例如以下代码就无法通过编译:

arduino 复制代码
using namespace std;

class A{
public:
    A(){

    }
    ~A(){

    }
private:
    int b;
};

int main() {
    std::map<A,int> m; //默认无参构造
    A a{};
    m[a] = 20;
    return 0;
}

因为上面的代码使用自定义的类A的对象作为map的key,但是类A却没有重载操作符<,此时我们可以可以通过重载函数的方式让其通过编译:

arduino 复制代码
bool operator< (const A& va) const{
    return b < va.b;
}

插入删除

对于std::map的访问,我们可以通过下标访问符[]或者函数at进行访问。

c 复制代码
int main() {
    std::map<int,std::string> m;
    m[1] = "hello";
    m[2] = "world";
    // 输出 hello
    std::cout << m[1] << std::endl;
    // 输出 world
    std::cout << m.at(2) << std::endl;
    return 0;
}

但是需要注意的一点是下标访问符[]或者函数at并不是完全等价的,下标访问符[]如果没有相关key也不会跑出异常, 而函数at需要确保一定包含相关的key,否则会抛出异常,这可能是大多数开发者不喜欢用at的原因吧。

对于std::map插入元素,我们可以使用下标访问符[]、函数at、函数insert进行添加:

c 复制代码
int main() {
    std::map<int,std::string> m;
    // 使用下标访问符添加
    m[1] = "hello";
    // 使用at
    // m.at(2) = "world"; // error除非有这个key
    m.at(1) = "world";
    // 使用insert
    m.insert(std::pair<int, string>(3, "gogo"));
    m.insert(make_pair(4,"666"));
    // 这个insert真不喜欢啊
    m.insert(map<int,string>::value_type (5,"5555"));
    for (int i = 0; i < 6; ++i) {
        std::cout << m[i] << std::endl;
    }
    return 0;
}

注意:当使用下标访问符时如果map中不存在某个key时,对map使用map[key]操作会在map中增加一个键值对,键名为key,值是传入的value类型的默认值。

在这里提以下std::pair 是 C++ 标准库中定义的一个模板类,用于将两个值组合为一个类型。它提供了简单的方式来存储和操作两个不同类型的值。 我们可以通过firstsecond成员变量,分别访问第一个和第二个值。

对于std::map的元素删除我们使用函数erase即可,如果需要清空则使用clear函数。

c 复制代码
int main() {
    std::map<int,std::string> m;
    // 输出0
    std::cout << "key的个数:" << m.count(5) << std::endl;
    m[5];
    // 输出1
    std::cout << "key的个数:" << m.count(5) << std::endl;
    // 删除key为5的
    m.erase(5);
    // 输出0
    std::cout << "key的个数:" << m.count(5) << std::endl;
    // 清空
    m.clear();
    return 0;
}

查找

在std::map中我们可以通过函数find或者函数count找到对应的key是否存在。

需要注意的是这里这里的函数find是指map的内部函数find,并不是指std::find,它们之间的性能是有区别的。内部函数find性能更佳。

c 复制代码
int main() {
    std::map<int,std::string> m;
    // 输出0
    m[1] = "hello";
    std::cout << "key count:" << m.count(1) << std::endl;
    if(m.find(1) != m.end()){
        std::cout << "找到key" << std::endl;
    } else{
        std::cout << "没有找到key" << std::endl;
    }
    return 0;
}

那么同样能实现查找功能,函数find和函数count应该用那个呢?无论是实际情况还是从字面意思理解都是用函数find更佳。函数count 虽然也能实现查找功能,但是它的更多是实现key的个数统计功能。

元素遍历

对于std::map的遍历一般有两种方式,一种是迭代器法,一种是auto循环。

c 复制代码
int main() {
    std::map<int,std::string> m;
    m[1] = "hello";
    m[2] = "world";
    // 迭代器法遍历
    for(map<int, string>::iterator it = m.begin(); it != m.end(); ++ it){
        cout << "key:" << it->first << "---value:" << it->second << std::endl;
    }
    cout << "+++++++++华丽的分割线+++++++++"<< std::endl;
    // auto遍历 
    for(const auto &pa : m){
        cout << "key:" << pa.first << "---value:" << pa.second << std::endl;
    }
    return 0;
}

排序规则

map容器排序是按照键的自动排序进行存储。默认情况下,std::map使用键的升序进行排序,也就是key从小到大。 但如果你希望按照其他方式进行排序,可以通过自定义比较函数来实现:一般借用仿函数即可,需要在构造map时传入我们放函数自定义的类或结构体。

c 复制代码
class MyCompare
{
public:
    // 这个值的类型和你map的key的类型对应
    bool operator()(int v1, int v2) const{
        return v1 > v2;
    }
};

int main() {
    std::map<int,std::string> m;
    m[1] = "hello";
    m[2] = "world";
    // auto遍历
    for(const auto &pa : m){
        cout << "key:" << pa.first << "---value:" << pa.second << std::endl;
    }
    cout << "+++++++++华丽的分割线+++++++++"<< std::endl;

    std::map<int,std::string,MyCompare> m2;
    m2.insert(m.begin(),m.end());
    for(const auto &pa : m2){
        cout << "m2的健值对key:" << pa.first << "---value:" << pa.second << std::endl;
    }
    return 0;
}

unordered_map

和map一样unordered_map也是STL中关联容器,但是unordered_map的key是没有排序的,unordered_map底层实现是哈希表,元素存储时没有特定的顺序,不保证元素以任何特定的顺序排列。

就性能而言因为map的底层是基于红黑树实现的,因此查找操作的平均时间复杂度为O(log n)。而unordered_map底层是哈希表,查找操作的平均时间复杂度为O(1),但最坏情况下为O(n)。

unordered_map的使用方式和map差不多,在实际的开发过程中选择使用map还是unordered_map取决于具体的应用场景和对容器操作的需求, 如果需要有序的键值对,则可以选择map;如果不需要有序的健值对则可以选择unordered_map,因为它的性能更佳。

关注我,后期不定期更新...

相关推荐
一律清风3 小时前
QT-文件创建时间修改器
c++·qt
风清扬_jd3 小时前
Chromium 如何定义一个chrome.settingsPrivate接口给前端调用c++
前端·c++·chrome
Death2004 小时前
Qt 6 相比 Qt 5 的主要提升与更新
开发语言·c++·qt·交互·数据可视化
麻辣韭菜6 小时前
网络基础 【HTTP】
网络·c++·http
阿史大杯茶6 小时前
Codeforces Round 976 (Div. 2 ABCDE题)视频讲解
数据结构·c++·算法
转调7 小时前
每日一练:地下城游戏
开发语言·c++·算法·leetcode
wdxylb7 小时前
使用C++的OpenSSL 库实现 AES 加密和解密文件
开发语言·c++·算法
CSP126368 小时前
特别节目————集训总结
c++
程序猿阿伟8 小时前
《C++游戏人工智能开发:开启智能游戏新纪元》
c++·人工智能·游戏
一线青少年编程教师8 小时前
线性表三——队列queue
数据结构·c++·算法