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

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

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

某日二师兄参加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++!(狗头)

内容概要:本文档定义了一个名为 `xxx_SCustSuplier_info` 的视图,用于整合和展示客户(Customer)和供应商(Supplier)的相关信息。视图通过连接多个表来获取组织单位、客户账户、站点使用、位置、财务代码组合等数据。对于客户部分,视图选择了与账单相关的记录,并提取了账单客户ID、账单站点ID、客户名称、账户名称、站点代码、状态、付款条款等信息;对于供应商部分,视图选择了有效的供应商及其站点信息,包括供应商ID、供应商名称、供应商编号、状态、付款条款、财务代码组合等。视图还通过外连接确保即使某些字段为空也能显示相关信息。 适合人群:熟悉Oracle ERP系统,尤其是应付账款(AP)和应收账款(AR)模块的数据库管理员或开发人员;需要查询和管理客户及供应商信息的业务分析师。 使用场景及目标:① 数据库管理员可以通过此视图快速查询客户和供应商的基本信息,包括账单信息、财务代码组合等;② 开发人员可以利用此视图进行报表开发或数据迁移;③ 业务分析师可以使用此视图进行数据分析,如信用评估、付款周期分析等。 阅读建议:由于该视图涉及多个表的复杂连接,建议读者先熟悉各个表的结构和关系,特别是 `hz_parties`、`hz_cust_accounts`、`ap_suppliers` 等核心表。此外,注意视图中使用的外连接(如 `gl_code_combinations_kfv` 表的连接),这可能会影响查询结果的完整性。
### STL 常见面试题及基础知识 #### 什么是智能指针?C++ 中有哪些常见的智能指针? 智能指针是一种用于管理动态分配内存的对象,它能够自动释放资源以防止内存泄漏。C++ 提供了几种标准库中的智能指针类型,分别是 `std::unique_ptr`、`std::shared_ptr` 和 `std::weak_ptr`[^1]。 - **`std::unique_ptr`**: 表示独占所有权的智能指针,不允许复制操作,但可以转移所有权。 - **`std::shared_ptr`**: 实现引用计数机制,允许多个指针共享同一个对象的所有权,当最后一个 `shared_ptr` 被销毁时才会删除底层对象。 - **`std::weak_ptr`**: 不增加引用计数,通常与 `shared_ptr` 配合使用,主要用于打破循环引用的情况。 以下是关于这些智能指针的一个简单代码示例: ```cpp #include <iostream> #include <memory> void example() { std::unique_ptr<int> uniquePtr = std::make_unique<int>(10); // 创建 unique_ptr std::cout << *uniquePtr << "\n"; std::shared_ptr<int> sharedPtr1 = std::make_shared<int>(20); // 创建 shared_ptr std::shared_ptr<int> sharedPtr2 = sharedPtr1; // 复制 shared_ptr,引用计数加一 std::cout << *sharedPtr1 << ", use count: " << sharedPtr1.use_count() << "\n"; std::weak_ptr<int> weakPtr = sharedPtr1; // 创建 weak_ptr if (auto lockedPtr = weakPtr.lock()) { // 锁定 weak_ptr 并检查有效性 std::cout << *lockedPtr << "\n"; } } ``` --- #### 容器之间的区别是什么? STL 提供了多种容器类型,每种都有其特定用途和性能特点。以下是一些常用容器及其主要特性: - **顺序容器**: - **`vector`**: 动态数组,支持随机访问,但在中间插入或删除效率较低。 - **`list`**: 双向链表,适合频繁插入和删除操作,不支持随机访问。 - **`deque`**: 双端队列,允许高效的两端插入/删除操作。 - **关联容器**: - **`set/multiset`**: 存储唯一键值(或多键值),内部实现为红黑树结构,按升序排列。 - **`map/multimap`**: 关联映射,存储键值对,按键排序。 - **无序容器**(哈希表): - **`unordered_set/unordered_multiset`**: 使用哈希函数存储数据,查找速度更快,但无法保证有序性。 - **`unordered_map/unordered_multimap`**: 类似于 map 的功能,但基于哈希表实现。 下面展示了一个简单的例子来比较不同容器的行为: ```cpp #include <iostream> #include <vector> #include <list> #include <unordered_set> int main() { std::vector<int> vec = {3, 1, 4}; std::list<int> lst = {1, 5, 9}; std::unordered_set<int> uset; for(auto &num : vec){ uset.insert(num); } for(auto &num : lst){ uset.insert(num); } for(const auto& num : uset){ // 输出去重后的集合 std::cout << num << ' '; } return 0; } ``` --- #### 如何优化 STL 容器的性能? 为了提高程序运行效率,可以从以下几个方面入手调整 STL 容器的应用方式: - 对于 `vector` 或其他动态扩展容器,在已知大小的情况下提前预留空间 (`reserve`) 减少重新分配次数。 - 尽量减少不必要的拷贝动作,优先考虑移动语义或者常量引用传递参数。 - 如果需要大量随机读取而较少修改,则选用连续存储结构如 `vector`; 若更多涉及增删节点则推荐采用分离式设计比如 `list`. --- #### 快速排序如何改进以适应实际场景需求? 尽管经典版本已经非常高效,但仍存在一些潜在问题需要注意处理: - 当输入规模较大且分布极端不平衡(例如接近完全逆序状态)的时候可能会退化成 O(n²),因此引入随机选取枢纽元策略有助于缓解此状况; - 此外由于递归调用层次较深可能导致堆栈溢出风险增大,故建议设置最大递归深度限制并改用迭代形式完成剩余部分工作 [^3]. ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

二进制架构

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

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

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

打赏作者

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

抵扣说明:

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

余额充值