第二章:通用数据结构 II——索引结构(在数据之上建立搜索路径)
绪论:从存储问题到定位问题
- 第一章解决的问题是:数据如何存放。
- 但存储方式只能决定“放在哪里” ,并不能解决“如何快速找到”。
当数据规模扩大时,如果查找仍依赖遍历:
- 访问成本保持在
O(n) - 并随数据规模 线性增长
因此系统需要一种新的结构,使查找过程不再检查全部数据,而是:
逐步缩小可能的搜索范围。
这种用于组织查找路径的结构,被称为:
索引结构
-
**定义:**索引结构是在既有存储结构之上建立 搜索路径,从而在查找过程中 逐步压缩搜索空间。
-
**基本模型:**根据索引与数据之间的关系,索引结构可以归纳为两种基本模型:
- 外部索引:在原有存储结构之外建立独立的定位层
- 内部索引:在数据结构内部组织查找路径
一、外部索引模型:存储与定位分离
定义
- 外部索引将 数据存储 与 数据定位 分离。
- 数据保存在主结构中,同时维护一套独立的
Key → Address映射结构。
机制
-
结构:系统维护两套结构:
- **主结构:**负责存储数据
- 索引结构:负责定位数据
-
查找流程:
- 通过索引结构定位目标位置
- 根据定位结果回到主结构读取数据
特征
- 主结构保持稳定
- 索引层可独立扩展,查找能力可单独增强
- 索引结构需要额外存储空间
- 更新操作需要同步维护索引
范围说明
- 外部索引描述的是一种 结构组织方式。
- 具体索引形态通常高度依赖系统场景,因此在此只给出抽象模型,后续在具体系统设计中展开。
总结
- 外部索引通过 独立定位结构 提升查找效率,同时保持主结构不变。
- 该模型在数据库与存储系统中非常常见。
二、内部索引模型:将查找嵌入结构
定义
- 与外部索引不同,内部索引不建立独立定位层,而是 直接改变数据组织方式。
机制
-
查找路径被嵌入结构内部:
- 数据组织方式决定查找过程
- 查找路径成为结构的一部分
结构原则:压缩搜索空间
-
内部索引的核心目标是:通过结构组织压缩搜索空间。
-
本质问题是:
如何在查找过程中尽可能减少需要访问的数据范围。
-
不同结构通过不同方式实现这一目标。
组织方式:三类压缩路径
-
根据压缩搜索空间的方式不同,内部索引可以归纳为三类结构:
- 有序索引
- 分层索引
- 分块索引
三、有序索引
- 有序索引通过 保持数据整体有序,使查找范围不断缩小。
- 核心思想是:通过顺序关系压缩搜索空间,二分查找是最典型实现。
A. 有序数组
定义
- 数据按键值顺序存储在连续数组中。
机制
- 查找通过 二分查找 实现,每次比较缩小一半搜索空间。
特征
- 查找复杂度:
O(log n) - 更新复杂度:
O(n) - 连续存储,缓存局部性好
总结
- 有序数组通过 全局有序 + 二分查找 提供
O(log n)查找能力。 - 更新成本高,更适合 静态数据集合。
- 在工程实践中,有序数组常用于数据规模稳定、更新较少的场景(如只读索引、静态查找表)
B. 树结构(平衡搜索树)
定义
- 将 二分查找思想 固化为树形结构。
机制
-
结构
- 每个节点包含左右指针
- 左子树键值小于当前节点,右子树键值大于当前节点
- 保持全局有序
-
查找流程:
- 从根结点开始比较
- 沿分支向下,小于进入左子树
- 查找路径长度等于 树高。
-
更新流程:
- 插入 按有序规则 找到位置
- 删除时用前驱/后继替换
- 为避免退化,通过旋转保持树高在
O(log n)
特征
- 查找复杂度:
O(log n) - 更新复杂度:
O(log n) - 为保持平衡,需要结构维护(旋转)
- 节点间通过指针连接,缓存局部性弱于连续结构
总结
- 平衡搜索树通过 有序结构 + 受控树高 提供稳定的
O(log n)查找与更新能力。 - 这种结构将 二分查找思想 扩展到动态数据集合,但也因此引入了结构维护成本
- 在工程实践中,树结构常通过 减少指针跳转、增强局部连续性 等方式优化缓存表现。
四、分层索引
- 分层索引通过 建立多级索引层,跨越更大的数据区间,再逐层逼近目标。
跳表
定义
- 在有序链表之上构建 多级索引层,将线性遍历转化为分层跳跃
机制
-
结构:
- 底层:完整有序链表,存储全部元素
- 高层:稀疏索引链表,用于跨区间跳跃
-
查找流程:
- 从最高层横向移动
- 无法继续前进时下降一层
- 最终在底层完成精确定位
-
更新流程:
- 插入:在底层插入,并随机决定节点层数
- 删除:同时移除各层索引节点
特征
- 查找/插入/删除:平均
O(log n) - 用概率平衡替代严格平衡,维护成本低
- 指针离散,缓存局部性弱于数组
总结
- 跳表通过 分层索引 → 跨区间跳跃 → 逐层逼近,将链表查找从
O(n)降到平均O(log n)。 - 相比平衡树,它以 概率平衡 替代 严格平衡,结构更简单。
- 在工程实践中,跳表常用于内存型有序索引结构(如 Redis Sorted Set),其结构简单、更新成本低,并且易于支持并发访问。
五、分块索引
- 分块索引通过 局部连续存储 减少指针跳转,并在块内进行局部搜索。
块状结构
定义
- 在链表结构中引入 局部连续存储:每个节点内部维护一个小数组(块)。
机制
-
结构:
- 块内:为一个连续数组,存储多个元素
- 块间:通过指针连接
-
结构:先沿链表定位目标块,再在块内顺序或二分查找。
-
更新流程:
- 插入/删除发生在块内,需移动块内元素
- 块满触发分裂,块空触发合并
- 维护集中在块级别
特征
- 查找复杂度:
O(n / B + log B)(B 为块大小) - 插入/删除复杂度:通常为
O(B) - 块大小通常控制在合适范围,以平衡访问与维护成本。
- 块内连续提升缓存命中率
- 指针跳转次数明显减少
总结
- 块状结构通过 块内连续 + 块间链接,在数组局部性与链表灵活性之间取得工程折中。
- 本质上是一种 链表块化的工程优化
- 在工程实践中,块状链表常以“分块 / 分段容器”的形式出现,通过块内连续存储提升遍历与缓存效率,同时保留块间链接带来的扩展与局部修改能力。
本章总结
-
第一章解决的是:数据如何存放。
-
本章解决的是:如何通过结构组织减少查找范围。
-
为此系统在存储结构之上建立 索引结构,通过搜索路径逐步压缩搜索空间。
-
内部索引常见三类压缩路径:
- 有序索引:保持有序,通过比较缩小范围
- 分层索引:多级路径跨区间跳跃
- 分块索引:局部连续减少跳转,块内局部搜索
-
这些结构的共同特点是:查找仍需要沿搜索路径逐步比较。
下一问题
-
索引结构通过搜索路径压缩搜索空间,但查找仍依赖逐层比较。
-
是否存在一种结构,可以通过 计算 直接确定数据位置,从而绕开搜索路径?
下一章讨论:映射结构。