在C++中,unordered_mapmap 都是用于存储键值对的关联容器,但它们在多个方面存在明显区别,下面为你详细介绍:

1. 底层数据结构

  • mapmap 基于红黑树(一种自平衡的二叉搜索树)实现。红黑树的每个节点包含一个键值对,并且左子树节点的键小于根节点的键,右子树节点的键大于根节点的键,同时通过一些规则(如节点颜色、旋转操作等)保证树的高度平衡,从而保证插入、删除和查找操作的时间复杂度稳定。
  • unordered_mapunordered_map 基于哈希表实现。哈希表通过哈希函数将键映射到一个固定大小的数组(哈希桶)中,每个桶可以存储一个或多个键值对。当发生哈希冲突(即不同的键映射到同一个桶)时,通常使用链表或红黑树来处理冲突。

2. 元素顺序

  • map:元素会按照键的大小进行排序,默认使用 < 运算符进行比较。这意味着当你遍历 map 时,元素会按照键的升序排列。例如,如果你插入键为 {3, 1, 2} 的元素,遍历 map 时会按照 1, 2, 3 的顺序输出。
  • unordered_map:元素是无序的,不会按照键的任何顺序进行存储。元素的存储位置取决于哈希函数的计算结果,因此遍历 unordered_map 时元素的顺序是不确定的。

3. 时间复杂度

  • 插入操作
    • map:插入操作的时间复杂度为 O(logn)O(\log n),因为在红黑树中插入一个节点需要进行平衡操作,以保证树的高度平衡。
    • unordered_map:平均情况下,插入操作的时间复杂度为 O(1)O(1),因为哈希表可以直接通过哈希函数计算出元素的存储位置。但在最坏情况下(如哈希冲突严重),时间复杂度可能会达到 O(n)O(n)
  • 查找操作
    • map:查找操作的时间复杂度为 O(logn)O(\log n),因为需要在红黑树中进行二分查找。
    • unordered_map:平均情况下,查找操作的时间复杂度为 O(1)O(1),通过哈希函数直接定位到元素所在的桶。最坏情况下时间复杂度为 O(n)O(n)
  • 删除操作
    • map:删除操作的时间复杂度为 O(logn)O(\log n),因为需要在红黑树中找到要删除的节点并进行平衡操作。
    • unordered_map:平均情况下,删除操作的时间复杂度为 O(1)O(1),但最坏情况下为 O(n)O(n)

4. 空间复杂度

  • map:由于红黑树需要额外的指针来维护节点之间的关系,因此空间复杂度相对较高,大约为 O(n)O(n)
  • unordered_map:哈希表需要一个固定大小的数组(哈希桶),并且在处理哈希冲突时可能需要额外的空间(如链表或红黑树),因此空间复杂度也为 O(n)O(n),但通常会比 map 占用更多的空间。

5. 迭代器

  • mapmap 的迭代器是双向迭代器,支持递增和递减操作,可以方便地遍历有序的元素。
  • unordered_mapunordered_map 的迭代器是前向迭代器,只支持递增操作,并且由于元素无序,迭代器的遍历顺序是不确定的。

6. 适用场景

  • map:适用于需要元素按照键的顺序进行遍历的场景,例如需要对数据进行排序输出、范围查找等。此外,当对数据的有序性有要求,并且插入、删除和查找操作的时间复杂度可以接受 O(logn)O(\log n) 时,使用 map 更为合适。
  • unordered_map:适用于对元素顺序没有要求,更注重插入、查找和删除操作的平均时间复杂度为 O(1)O(1) 的场景,例如缓存系统、哈希表实现等。

示例代码

#include <iostream>
#include <map>
#include <unordered_map>

int main() {
    // 使用 map
    std::map<int, int> orderedMap;
    orderedMap[3] = 30;
    orderedMap[1] = 10;
    orderedMap[2] = 20;
    std::cout << "Map (ordered by key):" << std::endl;
    for (const auto& pair : orderedMap) {
        std::cout << pair.first << ": " << pair.second << std::endl;
    }

    // 使用 unordered_map
    std::unordered_map<int, int> unorderedMap;
    unorderedMap[3] = 30;
    unorderedMap[1] = 10;
    unorderedMap[2] = 20;
    std::cout << "\nUnordered map (unordered):" << std::endl;
    for (const auto& pair : unorderedMap) {
        std::cout << pair.first << ": " << pair.second << std::endl;
    }

    return 0;
}

在上述代码中,map 会按照键的升序输出元素,而 unordered_map 的输出顺序是不确定的。

1 条评论

  • @ 2025-5-1 14:53:58

    unordered_map 是C++标准模板库(STL)中的关联容器,在 <unordered_map> 头文件中,位于 std 命名空间 。以下是它的详细介绍:

    特性

    • 关联性:以键值对(pair 类型)形式存储数据 ,通过键快速检索对应的值,而非像顺序容器那样通过绝对地址访问 。例如 unordered_map<string, int> umap; ,可通过字符串键找对应的整数值 。
    • 无序性:内部采用哈希表存储结构 ,元素不会按键值或映射值的特定顺序排序 。区别于 map (内部用红黑树实现,元素有序) 。
    • 键唯一性:键不能重复,不存在两个键相同的元素 。
    • 动态内存管理:利用内存管理模型动态分配和管理内存空间 。

    工作原理

    借助哈希函数,把键映射到哈希桶(数组中的位置) 。插入新键值对时,计算键的哈希值确定桶,然后将键值对存于该桶 。查找键时,同样先算哈希值定位桶,再在桶内找目标键 。因不同键可能哈希到同一桶(哈希冲突),桶内常以链表或红黑树等结构组织元素 。

    常用操作

    • 插入元素:可使用 insert 成员函数 ,如 umap.insert({ "key", 1 }); ;也能用 [] 操作符,像 umap["key"] = 1; (若键不存在则插入新键值对,存在则更新值 ) 。
    • 访问元素:通过 [] 操作符或 at 成员函数 ,如 int value = umap["key"];at 函数在键不存在时会抛 out_of_range 异常 。
    • 查找元素:用 find 成员函数 ,如 auto it = umap.find("key"); ,找到返回指向元素的迭代器,否则返回 end() 迭代器 。
    • 删除元素:可按键删除,如 umap.erase("key"); ;也能按迭代器指定位置或范围删除 。
    • 其他操作size 函数获取元素个数 ,empty 函数判断容器是否为空 ,clear 函数清空容器等 。

    构造方式

    • 默认构造std::unordered_map<key_type, value_type> umap;
    • 初始化列表构造std::unordered_map<std::string, int> umap = { { "one", 1 }, { "two", 2 } };
    • 拷贝构造std::unordered_map<std::string, int> umap2(umap);umap 为同类型容器 )

    优缺点

    • 优点: 平均情况下,查找、插入和删除操作时间复杂度为 O(1)O(1) ,数据量大时效率优势明显;可自定义哈希函数优化查找 。
    • 缺点:哈希表构建较耗时;因哈希冲突和额外存储结构(如链表) ,空间占用可能比一些有序容器大;元素无顺序,对有序性有要求场景不适用 。

    适用场景

    适用于无需元素有序,更看重快速查找、插入和删除操作的场景 。如缓存系统(快速查找缓存数据 ) 、统计单词出现次数(快速插入和更新计数 ) 、实现哈希表相关功能等 。

    • 1