Create hash.md, not finished yet.
This commit is contained in:
50
thu_dsa/chp9/hash.md
Normal file
50
thu_dsa/chp9/hash.md
Normal file
@@ -0,0 +1,50 @@
|
||||
Conclusions on HashTable
|
||||
========================
|
||||
|
||||
## 散列的基本概念
|
||||
|
||||
> 什么是散列?为什么需要散列?
|
||||
|
||||
据邓公所说,散列是一种思想。与已经学过的其他数据结构相比较,向量是采用循秩访问(call by rank)的访问方式,列表是采用循位置访问(call by position)的访问方式,二叉搜索树是采用循关键码访问(call by key)的访问方式,散列与他们都不一样,是采用循值访问(call by value)的访问方式。
|
||||
|
||||
举个例子,你现在身处同济大学嘉定校区,四周是一片荒芒,这个时候你想回家了。沿世界上所有的街道一间一间房找过去,这是循秩访问;你记得你家是住在四川省成都市某街道多少号,然后你可以依次先到四川省,再到成都市,再到某条街道,然后找到你家,这是循关键码访问;而循值访问,则是你通常会采用的方法——你根本不用去回想我家的地址是多少,你知道它就在那里,就在家这个词刚刚出现在你的脑海中的时候。想到家乡,你想到的不是地址或者一串数字,而是一个生动的影像,包含它的环境,四周的风物,已经曾经的朋友。这就是循值访问。
|
||||
|
||||
可以看到,相对于其他的访问方式,循值访问是将被访问对象的数值,与它在容器中的位置之间,直接建立了一个映射关系,从而对于任何对象的基本操作(访问,插入,删除)都只需要常数$O(1)$的时间,达到了最理想的境地。这就是人类需要散列的原因,你无法不被如此的诱惑所吸引。
|
||||
|
||||
> 完美散列
|
||||
|
||||
在时间与空间性能上均达到完美的散列,称为完美散列。
|
||||
|
||||
也就是说,对于完美散列,其中的每一个值,都可以唯一地映射到散列表中的一个位置,既无空余,亦无重复。从映射角度来看,完美散列是一个单射,同时也是一个满射。Bitmap就是完美散列的一个例子。
|
||||
|
||||
可以看出,完美散列实际中并不常见,在大多数的情形下,关键码的取值是远远大于词条的个数的,设关键码的取值为$[0, R)$, 词条的个数为$N$,则$R >> N$。设散列表的大小为$M$,此时,从定义域$[0, R)$到值域$[0, M)$的映射不可能是单射,即不可避免地会出现不同的关键码映射到散列表中的同一个位置,即所谓冲突。因此就需要合理地选择这一个映射关系,即散列函数,使冲突出现的可能性最小;同时还应该事先约定好一旦出现这种冲突,应该采取的解决访问。这两个问题将在下面重点讨论,即散列函数的设计与冲突解决方案。
|
||||
|
||||
## 散列函数的设计
|
||||
|
||||
> 散列函数的设计方案?什么是好的散列函数?
|
||||
|
||||
前面提到,从词条空间到地址空间的映射,即散列函数,绝对不可能是单射,冲突是一定不可能避免的,但是好的散列函数应该保证尽可能地少出现冲突。由此,可以提炼出散列函数的几个设计指标。
|
||||
|
||||
+ 确定性。散列函数确定的条件下,同一个关键码应该总是映射到同一个地址,这样才满足一个函数的定义。
|
||||
+ 快速性。是指散列地址的计算过程要尽可能快,要能在常数时间内完成。
|
||||
+ 满射。好的散列函数最好是一个满射,这样可以充分利用散列空间,尽可能地减少冲突的发生。
|
||||
+ 均匀性。也是为了减少冲突的发生,关键码被映射到各个散列地址的概率应该接近于$1/M$,这样可以防止汇聚(clustering)现象的发生,即关键码只被映射到少数的几个散列地址,在局部加剧散列冲突,全局的散列空间也没有得到充分地利用。
|
||||
|
||||
### 几个散列函数的实例
|
||||
|
||||
> 除余法(division method)
|
||||
|
||||
除余法的整体思路非常简单,即用关键码的值对散列表的长度$M$取余,即$hash(key) = key mod M$,这样可以将关键码映射到整个散列空间上。
|
||||
|
||||
这里问题的关键在于散列表长度$M$的选择。考虑有一组数据,其中的关键码的固定步长$S$变化(实际中的数据往往就是这种形式的,而不是随机的,例如for循环一般就是固定步长的数据)。根据上面对散列函数设计要求的分析,我们是希望散列函数可以尽可能地减少冲突。设第$i$个数据与第$j$个数据的关键码发生冲突,即
|
||||
$$
|
||||
S\times i \equiv S\times j\ (mod M)
|
||||
$$
|
||||
即$Si$与$Sj$是同余类,所以
|
||||
$$
|
||||
S(j - i) \equiv 0 \ (mod M)
|
||||
$$
|
||||
由此可解得
|
||||
$$
|
||||
j - i = \frac{M}{gcd(M, S)}
|
||||
$$
|
||||
@@ -1,14 +0,0 @@
|
||||
Conclusions on Hashing
|
||||
======================
|
||||
|
||||
## 散列函数的设计
|
||||
散列函数无非一个映射,其功能无非是将词条空间中的元素映射到散列表地址空间,其中前者远远大于后者,所以绝不可能是一个单射
|
||||
好的散列函数
|
||||
- 确定性:同一关键码总是被映射到同一地址
|
||||
- 快速
|
||||
- 满射:充分利用散列空间
|
||||
- 均匀:避免汇聚clustering。
|
||||
散列函数
|
||||
- 除余法
|
||||
- MAD法
|
||||
- 折叠法
|
||||
Reference in New Issue
Block a user