面试题答案
一键面试可用于实现类似 map 键值对存储及高效键查询的数据结构
- 哈希表
- 键查询时间复杂度:平均情况下为 O(1),最坏情况下为 O(n) 。这是因为哈希表通过哈希函数将键映射到特定的存储位置,理想情况下可以直接定位到目标值。但当哈希冲突发生时,可能需要遍历冲突链表,极端情况下所有元素都冲突,就会退化为链表的查询复杂度。
- 优点:在平均情况下查询速度极快,适用于需要频繁查询且对数据顺序无要求的场景,如缓存系统、查找表等。
- 缺点:哈希表需要额外的空间用于存储哈希桶等结构,而且如果哈希函数设计不合理,会导致大量哈希冲突,严重降低查询性能。另外,哈希表不支持按顺序遍历键值对。
- 跳表
- 键查询时间复杂度:平均情况下为 O(log n) 。跳表是一种基于链表的多层数据结构,通过增加索引层,使得查询时可以快速跳过部分节点,类似于二分查找的思想。
- 优点:实现相对简单,相比红黑树等平衡二叉树,代码复杂度低。支持快速的插入、删除和查询操作,并且可以按顺序遍历键值对。
- 缺点:跳表的空间复杂度较高,因为需要额外的空间存储多层链表结构。而且跳表的性能依赖于随机层数生成算法,如果算法不合理,可能无法达到理想的 O(log n) 复杂度。
- B - 树(及变种 B + 树)
- 键查询时间复杂度:O(logmn),其中 m 是 B - 树的阶数。B - 树是一种多路平衡搜索树,每个节点可以有多个子节点,通过将数据分散存储在节点中,减少树的高度,从而提高查询效率。B + 树更是将所有数据存储在叶子节点,使得范围查询更加高效。
- 优点:适用于存储大量数据且需要磁盘 I/O 的场景,如数据库索引。因为 B - 树节点可以容纳多个键值对,减少了磁盘 I/O 的次数。B + 树尤其适合范围查询,因为叶子节点通过链表相连。
- 缺点:实现复杂,插入和删除操作可能需要进行大量的节点分裂和合并操作,维护成本高。
基于新数据结构实现类似 map 容器的设计
假设选择跳表来实现类似 map 容器。
- 接口设计
- 构造函数:初始化跳表容器,可以接受一个可选的参数,如初始层数或者期望的元素数量,用于优化跳表的创建。
- 插入函数:
insert(key, value)
,将键值对插入到跳表中。 - 查询函数:
find(key)
,根据键查找对应的值,如果找到返回值的引用,否则返回一个表示未找到的特殊值(如空指针或者默认值)。 - 删除函数:
erase(key)
,删除指定键的键值对。 - 遍历函数:提供迭代器相关的接口,如
begin()
和end()
,支持按顺序遍历键值对。
- 内部实现
- 节点结构:设计一个跳表节点类,包含键值对、多个指针(对应不同层数的下一个节点指针)。
- 跳表结构:包含头节点、当前层数、节点数量等成员变量。
- 插入操作:首先通过查询找到插入位置,然后插入新节点,并根据随机层数生成算法决定是否提升节点的层数。如果需要提升层数,可能需要调整头节点的指针。
- 查询操作:从跳表的最高层开始,逐层向下查找,直到找到目标键或者确定键不存在。
- 删除操作:找到要删除的节点,删除该节点,并调整相关指针。如果删除节点后导致某些层的节点数量过少,可以适当降低跳表的层数。
- 随机层数生成:使用一个概率算法,如抛硬币法(每次抛硬币决定是否增加一层,假设概率为 0.5)来决定新插入节点的层数,以保证跳表的平衡性。