1. 静态哈希
我们平常用的 hash 都是静态 hash,比如将一个数字存入 hash 表中,需要先经过 hash 函数计算出 hash 值,根据 hash 值将其存入 hash 表中。这个 hash 表是一个静态的、大小固定的 hash 表,随着我们存入的数据越来越多,hash 表的访问也越来越慢,冲突率也越来越大。所以我们最好维持 hash 表中元素个数占 hash 表容量的百分之七十,这样才能保持低的冲突率。此时我们就需要用到动态 hash,也就是可扩展 hash。
2. 可扩展哈希概述
可扩展哈希是一种动态哈希,可扩展哈希的哈希函数也是动态变化的。
主要技术:directory 和 bucket
-
directories:每一个 directory 都有一个唯一的 id,当进行哈希扩展时 direcotry 的 id 可能会改变。directory 中存储的是对应 bucket 的指针。哈希函数返回 directory 的 id,用于找到对应的 bucket。directory 的数目 = 2 ^ global depth。
-
buckets: 存储实际数据。多个 directory 指针可能指向同一个 bucket。
3. 具体介绍
常用术语:
- global depth:与 directories 关联,global depth 等于 id(二进制串)的位长。
- local depth:与每一个 bucket 关联,local depth 等于对应 bucket 哈希值的位长。local depth 总是小于等于 global depth。若一个 bucket 的 local depth 小于 global depth,那么有多个 directoy 指向该 bucket。
- bucket splitting:当元素数量超过桶大小时,会进行桶分裂,将此桶分裂成两个桶。
- directory expansion:当发生溢出的桶的 local depth 等于 global depth 时会出现 directory expansion。
基本流程:
-
确定数据类型:整数、字符串、…
-
通过特定算法把数据编码成二进制串的形式
-
查看 global depth 和 directory id,根据实际数据的编码选择对应的 directory,获取对应的 bucket pointer
-
定位到对应的 bucket,并检查插入后桶大小是否超过桶容量(桶溢出)
- 若未超过,则直接插入成功
- 若超过桶容量,则进行 bucket splitting
-
bucket splitting 流程:
- 若 local depth 小于 global depth,此时肯定有多个 directory 指向发生溢出的 bucket,只需要将 local depth 加一,然后进行桶分裂
- 若 local depth 等于 global depth,此时需要将 global depth 加一,directories 的大小加倍(directory expansion),local depth 加一,进行桶分裂
4. 性能分析
4.1 优势
- 数据检索成本较低(就计算而言)。
- 没有数据丢失的问题,因为存储容量是动态增加的。
- 随着哈希函数的动态变化,关联的旧值将相对于新哈希函数进行重新哈希。
4.2 限制
- 如果在同一 directory 上散列多个记录,同时保持记录分布的不均匀性,则目录大小可能会显著增加。
- 每个 bucket 的容量都是固定的。
- 当全局深度和局部深度差变得很大时,指针会浪费内存。
- 这个方法编码起来很复杂。
加。
- 每个 bucket 的容量都是固定的。
- 当全局深度和局部深度差变得很大时,指针会浪费内存。
- 这个方法编码起来很复杂。