算法导论11-3
读书笔记
本小节介绍了散列表的灵魂--散列函数;
一个好的散列函数应满足简单均匀散列假设:每个关键字都被等可能地散列到\(m\)个槽位中的任何一个,并与其他关键字已散列到哪个槽位无关。
虽然上述假设难以实现,但是可以作为衡量其他散列函数的标准。
有一些动态集合的元素关键字并不是自然数,我们需要使用各种各样的方法将其变为自然数,以便进行散列;
除法散列法
说是除法,但实际上进行的却是取模操作,具体函数如下
其中\(m\)的取值有一些讲究,应当避免为\(2\)的幂;其取值应该为一个不太接近\(2\)的整数幂的素数。
乘法散列法
有两个步骤:
- 用关键字\(k\)乘以常数\(A(0<A<1)\),并提取\(kA\)的小数部分;
- 用\(m\)乘以这个小数,再向下取整;
具体函数如下:
乘法散列法的一个优点就是对\(m\)的选择不是特别关键,一般选择它为\(2^p\)。
全域散列法
事实上,我觉得全域散列法不能被称为散列方法,应该是一种散列策略;
全域散列法在执行之前,从一组散列函数中通过随机算法选出一种作为散列算法,然后进行散列;
课后习题
11.3-1
假设我们希望查找一个长度为\(n\)的链表,其中每一个元素都包含一个关键字\(k\)并具有散列值\(h(k)\)。每一个关键字都是长字符串。那么在表中查找具有给定关键字的元素时,如何利用各元素的散列值呢?
对链表中每一个元素,先比较该元素与待查关键字的散列值。若一样,在比较字符串值。
11.3-2
假设将一个长度为\(r\)的字符串散列到\(m\)个槽中,并将其视为一个以\(128\)为基数的数,要求应用除法散列法。我们可以很容易地把数\(m\)表示为一个\(32\)位的机器字,但对长度为\(r\)的字符串,由于它可以被当作以\(128\)为基数的数来处理,就要占用若干机器字。假设应用除法散列表来计算一个字符的散列值,那么如何才能在除了该串本身占用的空间外,只利用常数个机器字?
题目本身讲的有点绕,请参考这里
11.3-3
考虑除法散列法的另一个版本,其中\(h(k)= k \; mod \; m, m = 2^p -1\),\(k\)为按基数\(2^p\)表示的字符串。试证明:如果串\(x\)可由串\(y\)通过自身字符置换排列导出,则\(x\)和\(y\)具有相同的散列值。给出一个应用的例子,其中这一特性在散列函数中不希望出现的。
不会证明,但是这道题的出现说明需要注意\(m\)的取值;
11.3-4
考虑一个大小为\(m=1000\)的散列表和一个对应的散列函数\(h(k) = \lfloor m (kA \;mod \;1)\),其中\(A=(\sqrt{5} -1)/2\),尝试计算关键字\(61,62,63,64,65\)被映射到的位置。
k | h(k) |
---|---|
61 | 700 |
62 | 318 |
63 | 936 |
64 | 554 |
65 | 172 |