卡码笔记
首页
计算机基础
C++
Java
面经
笔记广场 (opens new window)
代码随想录 (opens new window)
首页
计算机基础
C++
Java
面经
笔记广场 (opens new window)
代码随想录 (opens new window)
  • 基础与语法

  • 面向对象

  • STL 与容器

    • STL容器了解哪些
    • STL中allocator的作用
    • STL中迭代器失效的场景
    • c++的map和unordered_map有什么区别和实现原理
      • 简要回答
      • 详细回答
      • 代码示例
      • 知识拓展
    • map,deque,list的底层实现原理
    • unordered_map的rehash机制
    • vector底层原理和扩容过程
    • push_back()和emplace_back()的区别
  • 内存管理

  • C++11 与现代 C++

  • 智能指针

  • 并发与 I/O

# c++的map和unordered_map有什么区别和实现原理

# 简要回答

map 是基于红黑树实现的有序关联容器,元素按 key 升序排列,查找、插入、删除复杂度为 O(logN)。

unordered_map 是基于哈希表实现的无序关联容器,元素无序存储,查找、插入、删除的平均复杂度为 O(1),最坏 O(n)。

# 详细回答

特性 map unordered_map
底层结构 红黑树(平衡二叉搜索树) 哈希表(通常是开放地址或链式哈希)
键值顺序 自动排序(升序) 无序
时间复杂度 O(logN) 查找、插入、删除 O(1) 平均,O(n) 最坏
是否支持自定义顺序 支持(自定义比较器) 不支持(但可自定义哈希函数)
内存使用 较紧凑 较大(因哈希表可能有空槽)
  • 适用场景

map: 当需要排序遍历所有键值对时,用map容器,但是此时键值对不支持哈希

unordered_map:当某一个场景下,只关心快速查找、插入性能时,使用unordered_map,此时有很多元素频繁查找。

# 代码示例

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

int main() {
    map<string, int> ordered_map;
    unordered_map<string, int> hash_map;

    ordered_map["apple"] = 1;
    ordered_map["banana"] = 2;
    ordered_map["orange"] = 3;

    hash_map["apple"] = 1;
    hash_map["banana"] = 2;
    hash_map["orange"] = 3;

    cout << "map(有序)遍历结果:" << endl;
    for (auto& p : ordered_map)
        cout << p.first << " => " << p.second << endl;

    cout << "\nunordered_map(无序)遍历结果:" << endl;
    for (auto& p : hash_map)
        cout << p.first << " => " << p.second << endl;

    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

运行上述代码后会得到:map 总是按照字典序输出,而 unordered_map 输出顺序每次都可能不一样

# 知识拓展

  • 多线程下的性能差异

unordered_map 插入查找快,但线程安全性差,需加锁或使用 concurrent_unordered_map(TBB)

map 插入较慢,但访问结构清晰,适合在多线程并发读的场景下做共享(读锁+写锁)

总之在多线程环境下,map 的结构更适合读多写少的场景,配合 shared_mutex 可以实现并发读。而 unordered_map 虽然查找快,但并不适合直接并发写操作,除非我们通过分桶加锁,或者使用像 TBB 提供的 concurrent_unordered_map 这样的专业并发容器

  • 知识图解

image B 表示黑色节点,R 表示红色节点(红黑树规则)

左子树所有键小于父节点,右子树所有键大于父节点

插入时保持二叉搜索树性质 + 红黑树平衡规则(旋转 + 变色)

访问:查找 pear:banana →orange→ pear (O(logN))

插入或删除时需要维护红黑树的平衡 → 旋转/染色 → 时间复杂度 O(logN)

image 哈希函数将 key 映射到桶索引(如 hash("apple") % 5 = 1)

多个 key 映射到相同桶时,使用链表或链式节点进行存储(如 pear 与 banana 哈希冲突)

插入时直接挂在链尾,查找遍历链表(冲突越多越慢)

访问:计算key的哈希值 → 得到桶号

查看该桶链表中是否已有 key → 有则更新,无则插入

若负载因子超过阈值 → 触发扩容 + 重新哈希(rehash)

  • 面试官可能追问

    1.unordered_map 发生哈希冲突会怎么处理?

当 unordered_map 发生哈希冲突时,会将多个哈希值相同的元素存入同一个哈希桶中,通常通过链表(链式地址法)或开放寻址法来解决;

C++ STL 中一般采用链表方式,即发生冲突的元素会插入到该桶对应链表的末尾,查找时需要遍历该链表,冲突多时会降低查找性能。

2.为什么 unordered_map 最坏情况是 O(n)?

因为 unordered_map 底层是哈希表结构,理论上查找和插入都是 O(1),但在最坏情况下,所有键的哈希值都一样(哈希冲突极端严重),所有元素都被插入到同一个桶中,退化成了链表结构,此时查找、插入、删除都变成了线性查找,时间复杂度退化为 O(n)。

3.map 和 unordered_map 的内存占用对比?

map 由于基于红黑树实现,每个节点需要存储指针(父节点、左右子节点)和颜色位,结构更紧凑且稳定;

而 unordered_map 基于哈希表实现,需要额外存储哈希桶数组(包含大量空位)、链表指针等结构,为了降低冲突还要预留扩容空间,因此在元素量相同时,unordered_map 的内存占用通常比 map 更高,但也换来了更快的查找效率(平均 O(1))。

4.哈希表的负载因子是啥?扩容机制如何?

哈希表的负载因子(Load Factor)是元素个数(N)与桶个数(B)之比,公式为:α = N / B,表示哈希表的“拥挤程度”;

当负载因子超过某个阈值(例如 0.75),就会触发扩容机制,即重新分配更大的桶数组并对所有元素重新哈希(rehash),以减少冲突、保持插入和查找的效率;

但扩容过程代价较大,会导致性能瞬时下降。

Last Updated: 3/10/2026, 6:08:48 PM

← STL中迭代器失效的场景 map,deque,list的底层实现原理 →

评论

验证登录状态...

侧边栏
夜间
卡码简历
代码随想录
卡码投递表🔥
2026群
添加客服微信 PS:通过微信后,请发送姓名-学校-年级-2026实习/校招
支持卡码笔记
鼓励/支持/赞赏Carl
1. 如果感觉本站对你很有帮助,也可以请Carl喝杯奶茶,金额大小不重要,心意已经收下
2. 希望大家都能梦想成真,有好的前程,加油💪