百度C++一面:map和unordered_map相关面试题 + 力扣49. 字母异位词分组(可优化)
百度C++一面:map和unordered_map相关面试题百度C++一面:map和unordered_map相关面试题
本文整理自百度 C++ 一面高频面试题,覆盖底层原理、性能对比、迭代器失效、扩容机制、线程安全等核心考点,建议收藏。
目录
一、底层实现
| 特性 | map | unordered_map |
|---|---|---|
| 底层数据结构 | 红黑树(自平衡二叉搜索树) | 哈希表(散列表) |
| 存储结构 | 树节点,按 key 大小有序排列 | 桶数组 + 开链法(链表挂桶) |
二、性能对比
| 操作 | map | unordered_map |
|---|---|---|
| 插入 | O(log n),稳定 | 平均 O(1),最坏 O(n) |
| 查找 | O(log n),稳定 | 平均 O(1),最坏 O(n) |
| 删除 | O(log n),稳定 | 平均 O(1),最坏 O(n) |
unordered_map 退化为 O(n) 的场景:
- 哈希函数质量差,大量 key 碰撞到同一个桶
- 故意构造的对抗性数据(哈希碰撞攻击)
- 极端情况下所有元素挤在一条链上,退化为链表遍历
三、有序性
map 有序:红黑树本质是二叉搜索树,中序遍历结果即 key 从小到大的有序序列。
unordered_map 无序:
- 元素存储位置由
hash(key) % 桶数量决定,与 key 大小无关 - 不同 key 的哈希值分布"随机",桶的下标也是"乱"的
- 扩容 rehash 后,元素位置会变化,遍历顺序可能前后不一致
💡 注意:unordered_map 的遍历有确定顺序(桶序 + 桶内链表序),但这不是"有序",只是存储顺序。
四、哈希冲突解决
STL unordered_map 采用开链法(链地址法)。
| 方法 | 原理 | 优点 | 缺点 |
|---|---|---|---|
| 开链法 | 每个桶挂一条链表,冲突元素追加到链上 | 删除简单;负载因子可超过 1 | 指针开销;缓存不友好 |
| 开放寻址 | 冲突时按探测策略找下一个空位置 | 缓存友好;无额外指针 | 删除需墓碑标记;负载因子不能高 |
五、自定义 Key 的要求
map 的 key 要求
需要定义 operator<(小于比较运算符),红黑树靠比较大小来排序和定位。
struct MyKey {
int a, b;
bool operator<(const MyKey& other) const {
if (a != other.a) return a < other.a;
return b < other.b;
}
};
std::map<MyKey, int> m; // ✅
力扣49. 字母异位词分组
方法一:排序
由于互为字母异位词的两个字符串包含的字母相同,因此对两个字符串分别进行排序之后得到的字符串一定是相同的,故可以将排序之后的字符串作为哈希表的键。
class Solution {
点击查看代码
class Solution {
public:
vector<vector<string>> groupAnagrams(vector<string>& strs) {
unordered_map<string, vector<string>> mp;
for (string& str: strs) {
string key = str;
sort(key.begin(), key.end());
mp[key].emplace_back(str);
}
vector<vector<string>> ans;
for (auto it = mp.begin(); it != mp.end(); ++it) {
ans.emplace_back(it->second);
}
return ans;
}
};
复杂度分析
-
时间复杂度:O(nklogk),其中 n 是 strs 中的字符串的数量,k 是 strs 中的字符串的的最大长度。需要遍历 n 个字符串,对于每个字符串,需要 O(klogk) 的时间进行排序以及 O(1) 的时间更新哈希表,因此总时间复杂度是 O(nklogk)。
-
空间复杂度:O(nk),其中 n 是 strs 中的字符串的数量,k 是 strs 中的字符串的的最大长度。需要用哈希表存储全部字符串。

浙公网安备 33010602011771号