C++面试八股文:知道std::unordered_set/std::unordered_map吗?

面试中,二师兄解释了unordered_set/unordered_map是C++11引入的无序容器,基于哈希表实现,提供常数时间复杂度的平均操作。虽然在某些场景下可以替代set和map,但由于最差时间复杂度为O(n),不适合对顺序有要求的场景。当使用自定义类型作为key时,需要提供哈希和相等比较函数。面试还提及了unordered_multiset/unordered_multimap,但未深入讨论。

某日二师兄参加XXX科技公司的C++工程师开发岗位第27面:

面试官:知道std::unordered_set/std::unordered_map吗?

二师兄:知道。两者都是C++11引入的新容器,和std::setstd::map功能类似,key唯一,unordered_mapvalue可变。

二师兄:不同于set/mapunordered_set/unordered_map都是无序容器。

面试官:那你知道它们底层怎么实现的吗?

二师兄:两者底层使用哈希表实现,因此插入、删除和查找操作的平均时间复杂度为常数时间O(1)

面试官:既然平均复杂度是O(1),那么是不是可以取代setmap了?

二师兄:这里是平均的时间复杂度。哈希表插入、删除和查找操作的最差时间复杂度是O(n),要比set/mapO(log n)大。

面试官:那你觉得哪些场景适合set/map,哪些场景适合unordered_set/unordered_map

二师兄:set/map适用于需要有序存储和快速查找的场景,而unordered_set/unordered_map适用于需要快速插入和查找的场景。

面试官:unordered_set/unordered_map对于key的类型有什么要求吗?

二师兄:因为unordered_set/unordered_map底层采用哈希表,所以在使用自定义类型作为key的时候,需要告诉编译器如何计算此类型的hash值,同时还要告诉编译器如何判断两个自定义类型的对象是否相等。以下代码无法通过编译:

#include <iostream>
#include <unordered_set>
struct Foo
{
    std::string str;
    int val;
};
int main(int argc, char const *argv[])
{
    std::unordered_set<Foo> uset;
    uset.insert({"42",42});
    uset.insert({"1024",1024});
    return 0;
}

二师兄:此时需要为Foo类型实现bool operator==(const Foo& o) const函数和size_t operator()(const Foo& f) const仿函数,才能通过编译:

#include <iostream>
#include <unordered_set>
struct Foo
{
    std::string str;
    int val;
    bool operator==(const Foo& o) const
    {
        return str == o.str && val == o.val;
    }
};
struct Hash
{
    size_t operator()(const Foo& f) const
    {
        return std::hash<std::string>()(f.str) ^ std::hash<int>()(f.val);
    }
};
int main(int argc, char const *argv[])
{
    std::unordered_set<Foo,Hash> uset;
    uset.insert({"42",42});
    uset.insert({"1024",1024});
    return 0;
}

二师兄:当然我们也可以使用std::function或者lambda来代替仿函数,目的都是为了使得编译器知道如何计算自定义类型的哈希值。

面试官:用过unordered_multiset/unordered_multimap吗?

二师兄:没用过。但是应该和multiset/multimap类似,只是底层也采用hash表实现。

面试官:好的,今天的面试就结束了,请回去等消息吧。

对于今天面试官的表现,小伙伴们能给几分呢?不是面试官要放水,面完set/map之后再面unordered_set/unordered_map,真的没有那么多好问题,因为两者太像了。。。

好了,今天的面试到这里就结束了,让我们期待明天面试官的表现吧~

关注我,带你21天“精通”C++!(狗头)

`std::map` 和 `std::unordered_map` 都是 C++ 标准库中的关联容器,用于存储键值对,但它们在内部实现、查找效率以及适用场景上有一些关键的区别: 1. **查找速度**: - `std::map`(也叫红黑树)使用平衡二叉搜索树(BST)作为底层数据结构,插入、删除和查找操作的时间复杂度通常是 O(log n),其中 n 是元素数量。因为它是有序的,所以查找时需要比较,适合有序的数据集合。 - `std::unordered_map`(哈希表)使用哈希表实现,查找、插入和删除的平均时间复杂度为 O(1)。无序,主要依赖哈希函数快速定位到存储位置,但在最坏的情况下(哈希冲突严重)时间复杂度会退化为 O(n)。 2. **空间占用**: - `std::map`由于需要维护元素的有序性,需要额外的空间用于平衡树的结构,所以空间利用率相对较低。 - `std::unordered_map`则没有这种额外开销,因为它依赖哈希表,空间效率更高。 3. **排序与稳定性**: - `std::map` 的元素是有序的,可以根据键进行自然排序或自定义比较器排序,保证了相等键的顺序不变。 - `std::unordered_map` 不保证元素顺序,插入和删除不会改变其他元素的顺序。 4. **碰撞处理**: - `std::map` 中的元素如果有相同的键,则根据实现(如STL的版本)选择其中一个插入。 - `std::unordered_map` 使用链地址法或开放寻址法解决哈希冲突,确保所有键都唯一。 5. **插入和删除性能**: - 当数据量大并且需要频繁插入或删除时,`std::unordered_map` 可能更快,因为它的插入和删除操作不涉及复杂的调整操作。 - 对于查找,如果数据大致均匀分布,`std::unordered_map` 更快;但如果数据有特定模式导致大量冲突,`std::map` 可能表现更好。 相关问题: 1. 哪种容器更适合数据量不大且查询次数较多的情况? 2. 如何在`std::map`和`std::unordered_map`之间选择,考虑的是哪种类型的查询为主? 3. 如果我需要保持元素的原始插入顺序,应该使用哪个容器?
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

二进制架构

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值