dictionary、map、associated array 以及 symbol table 都指同一个 ADT (Abstract Data Type),它维护者一个 items 集合,每个 item 对应唯一的 key,dictionary 需要满足:
insert(item)
:将 item 添加到集合中delete(item)
:从集合中删除 itemsearch(key)
:在集合中寻找 key 对应的 item,如果存在就返回其中,不同 items 对应的 key 不同,如果插入多个 key 相同的 items,则只有最后一个会被保留。dictionary 对 items 的顺序关系没有规定,利用 Balanced BSTs 可以解决 dictionary problem,其 insert、delete、search 的时间复杂度都是
Dictionaries 是计算机科学中应用范围最广的数据的结构之一。
最简单的方法就是将所有 items 存储在 linked list 中,但它的时间复杂度不满足要求:
Method | Average-case complexity | Worst-case complexity |
---|---|---|
insert(item) | ||
delete(item) | ||
search(key) |
在集合很小的情况下,这种实现方案性能不错,但随着数据量增加,它的 delete、search 时间复杂度也将线性增加。
linked list 的问题在于不支持 random access,那么为什么不用 direct access table 呢?如果 key 恰好是非负数,我们就能将 delete、search 的时间复杂度降到
将每个 items 的 key 先转化成非负整数,这样既可以解决负整数 key 问题,也能解决非整数类型的 key 问题。理论上,只要 items 集合是可数的 (countable),这一步肯定能实现。
如果能将 keys 的取值
由于
Chaining 就是将散列到同一个位置的 items 用 linked list 串联:
由于 search、delete 需要遍历 key/item 对应的 linked list,因此最差情况就是所有 keys/items 都被散列到同一个 slot,导致 search、delete 的时间复杂度为
什么样的 hash function 才是个优秀的 hash function 呢?一个常用的定义/假设就是:SUHA (Simple Uniform Hashing Assumption):同一个 key 被散列到每个 slot 的概率相等,且与其它 keys 的散列结果相互独立。
在 SUHA 下,我们定义:
于是 search、delete 的时间复杂度为
本节介绍 3 种在实践中能达到上述性能的 hash functions。实践中达到指的是在大多数 keys/items 集合上表现符合要求,理论上满足 SUHA 的只有 Universal Hashing。
Division method 就是选择一个足够大的数
Division method 在许多 keys/items 集合上能取得不错的散列效果。但 division 在现代计算机架构上的速度是 multiply 的
Multiplication method 的核心计算过程如下:
用类似乘法笔算的方式理解:
将多个平移版本的
取第 2 步中得到的数字的高
从图中可以看出:高
详情看这里