通用数据结构 III——映射结构(规模下的空间与过滤能力)

6 阅读8分钟

第三章:通用数据结构 III——映射结构(规模下的空间与过滤能力)

绪论:从搜索结构到计算结构

  • 第二章解决的问题是:如何减少查找过程中需要访问的数据数量

为此,系统在存储结构之上建立 索引结构,通过组织查找路径逐步压缩搜索空间。

常见方式包括:

  • 有序索引
  • 分层索引
  • 分块索引

通过这些结构,查找复杂度可以从 O(n) 降低到 O(log n)

但当数据规模继续扩大时,这种方法仍然存在新的限制:

  • 查找过程仍然需要 沿索引路径逐层比较
  • 随着数据规模扩大,这种路径搜索仍然会产生明显的访问成本

因此系统开始探索另一种思路:

是否可以通过计算直接确定数据位置,而不再依赖搜索路径?

如果数据位置可以通过函数计算得到,那么查找过程将不再需要逐层搜索。

这种通过 计算定位数据位置 的结构,被称为:

映射结构

定义

  • 通过函数或规则,将 Key 直接映射到数据位置或状态,从而减少或避免搜索过程。

核心思想

  • 通过计算替代搜索路径。

主要能力

  • 在大规模系统中,映射结构通常通过以下方式降低访问成本:
  1. 直接映射:通过函数计算直接定位数据(如哈希表)
  2. 空间压缩:通过位级结构降低存储成本(如位图)
  3. 概率过滤:通过概率模型快速判断存在性(如布隆过滤器)
  4. 规模估计:通过统计方法估算数据规模(如 HyperLogLog)

一、确定性映射:哈希表

定义

  • 一种通过 哈希函数将 Key 映射到固定地址范围 的数据结构。

核心思想

  • 通过函数计算直接确定数据位置,从而避免搜索过程。

结构

  • 底层通常为一个 数组
  • 每个数组槽位称为 桶(Bucket)
  • 每个 Key 通过哈希函数映射到一个桶

机制

  • 常见映射规则Index = Hash(Key) mod N

    • Hash(Key) 为哈希函数
    • N 为数组容量
  • 查找流程

    1. 计算 Key 的哈希值
    2. 定位到对应数组下标
    3. 若发生冲突,则在桶中继续查找
  • 插入流程

    1. 计算 Key 的哈希值
    2. 定位到桶
    3. 若发生冲突,则按照冲突处理策略存储元素
  • 扩容机制

    • 负载因子 过高时需要扩容

    • 扩容过程包括:

      1. 分配更大的数组
      2. 重新计算所有元素位置(重新哈希)
    • 扩容复杂度为:O(n)

哈希冲突

  • 原因

    • 哈希函数需要将 无限 Key 空间映射到有限数组空间
    • 根据 抽屉原理,不同 Key 必然可能映射到同一位置

常见冲突解决策略

  1. 拉链法(分离链接)

    • 结构:

      • 每个桶维护一个链表
      • 冲突元素存储在链表中
    • 工程优化:当链表长度超过阈值时,可转换为 红黑树

    • 特点

      • 插入稳定,实现简单
      • 节点离散存储,缓存局部性较差
    • 工程实践:Java HashMap

  2. 开放寻址

    • 结构:

      • 所有元素必须存储在数组中
      • 冲突时按规则寻找下一个空位置
    • 常见策略:

      • 线性探测
      • 二次探测
      • 双重哈希
    • 特点:

      • 数据物理连续,缓存友好
      • 容易产生 聚簇效应
    • 工程实践:Google flat_hash_map

    1. 布谷鸟哈希

      • 结构:

        • 每个元素拥有 K 个候选位置
        • 由不同哈希函数计算原始Key得到
      • 插入流程:新元素占位→原元素被踢出→原元素寻找新的候选位置→循环直到稳定或扩容

      • 特点:

        • 查询复杂度稳定接近 O(1)
        • 空间利用率高
        • k=3 时空间利用率可达到 90% 以上
      • 工程实践:高性能哈希表实现。

特征

  • 查找、插入复杂度:O(1)
  • 扩容复杂度:O(n)
  • 随机访问能力强
  • 不支持范围查询

总结

  • 哈希表通过:Key → Hash → Array Index ,实现 常数时间的数据定位能力
  • 在工程实践中,常用于:数据容器、缓存索引、高速查找结构。

二、位级压缩映射:位图

定义

  • 一种 使用 1 bit 表示一个状态 的数据结构。

结构

  • 底层为一个连续二进制数组
  • 每一位表示一个状态

机制

  • 核心映射关系:Integer → Bit Position
  • 定位方式:Index / 8 定位到字节,Index % 8 定位到具体的位。
  • 常见操作:设置状态、清除状态、判断状态
  • 集合运算:AND、OR、XOR

特征

  • 空间占用极小
  • 支持高速位运算
  • 只能表示有限范围整数集合
  • 不适用于稀疏数据

总结

  • 位图通过:整数 → 位位置,实现 极高密度的数据压缩存储
  • 在工程实践中,常用于:用户签到、在线状态、大规模布尔标记、集合交并统计

三、概率映射:布隆过滤器

定义

  • 一种 概率型集合结构,用于判断元素 是否可能存在

结构

  • 布隆过滤器由两部分组成:

    1. 一个 位数组
    2. K 个哈希函数
  • 每个元素会被映射到多个 bit 位置。

机制

  • 插入Key → k个哈希函数→ 对应bit置1

  • 查询

    • 若任意 bit 为 0一定不存在
    • 若全部为 1可能存在

特征

  • 空间占用极小
  • 查询速度极快
  • 存在 误判率
  • 标准版本 无法删除元素

总结

  • 布隆过滤器通过:Key → 多哈希函数 → 位数组 ,实现 极低空间成本下的存在性判断
  • 在工程实践中,常用于:数据库查询过滤、缓存穿透防护、存储系统预判。
  • 因此常被称为:存储系统的“门神” ,在访问昂贵资源前进行快速过滤。

四、统计映射:HyperLogLog

定义

  • 一种用于 集合基数估计(Cardinality Estimation) 的概率统计结构。
  • 用于在极小空间下估计:集合中不同元素的数量。

核心原理

  • 利用 哈希值前导零长度 估算基数

结构

  • HyperLogLog通常由两部分组成:

    1. 多个 计数桶
    2. 一个 哈希函数

机制

  • 基本过程:

    1. 对元素进行哈希
    2. 观察哈希值前导零长度
    3. 记录统计结果
    4. 通过概率模型估算集合规模

特征

  • 空间极小(KB级)
  • 支持海量数据统计
  • 结果为 近似值

总结

  • HyperLogLog通过:哈希 → 前导零统计 → 基数估计,实现 大规模集合统计能力
  • 在工程实践中,常用于:UV 统计、去重估计、大规模数据分析。
  • 通常会采用 分桶统计 以减少极端情况带来的误差。

五、映射稳定性:一致性哈希

定义

  • 一种用于保持 哈希映射稳定性 的算法
  • 主要用于 分布式系统中的数据分配

结构

  • 一致性哈希将哈希空间抽象为0 ~ 2^32 ,形成一个 虚拟环(Hash Ring)
  • 系统节点与数据 Key 都被映射到这个环上。

机制

  • 映射规则:

    1. 节点映射到环上
    2. Key 顺时针寻找最近节点
  • 顺时针规则保证:每个 Key 只属于一个连续区间

  • 当节点加入或离开时:只有相邻区间的数据需要重新分配

特征

  • 映射关系稳定
  • 扩容只影响局部数据

总结

  • 一致性哈希通过:Key → Hash Ring → Node ,实现 稳定的数据映射关系
  • 在工程实践中,通常引入 虚拟节点(Virtual Nodes) ,用于提升负载均衡能力

本章总结

  • 第一章解决的问题是:数据如何存放
  • 第二章解决的问题是:如何通过结构组织压缩搜索空间
  • 本章解决的问题是:如何通过计算降低大规模数据的访问成本

为此,系统引入了一类新的结构:映射结构

其核心思想是:

  • 通过计算替代搜索

这些结构通过函数计算、空间压缩或概率模型,使系统能够在有限资源下处理海量数据。

常见方式包括:

  1. 确定性映射:通过函数计算直接定位数据(如哈希表)
  2. 位级压缩:通过位级结构降低存储成本(如位图)
  3. 概率过滤:通过概率模型快速判断存在性(如布隆过滤器)
  4. 规模估计:通过统计方法估算集合规模(如 HyperLogLog)
  5. 稳定映射:在分布式环境中保持映射关系稳定(如一致性哈希)

这些结构的共同特点是:

通过计算、压缩或概率模型减少数据访问成本。

下一问题

前三章讨论的是 通用数据组织能力

  • 存储结构
  • 索引结构
  • 映射结构

这些结构提供了通用的数据组织方式,但在真实系统中,数据结构往往还会受到 具体场景约束 的影响。

当约束发生变化时,结构设计的目标也会随之改变,通用结构往往无法达到最佳性能。

因此,在许多系统中会出现 针对特定约束优化的数据结构

当问题的约束条件发生变化时,数据结构应该如何调整其组织方式?

接下来的章节将讨论这些 场景驱动的结构设计