0
点赞
收藏
分享

微信扫一扫

(2.5)查找之哈希查找


文章目录

  • ​​1.哈希函数的定义​​
  • ​​2.哈希表​​
  • ​​3.好的哈希函数特点​​
  • ​​4.常见的哈希函数的构造方法​​
  • ​​6.字符串的哈希查找​​

1.哈希函数的定义

  • 一般情况下,需在关键字与记录在表中的存储位置之间建立一个函数关系,以 H(key) 作为关键字为key 的记录在表中的位置,通常称这个函数 h(key) 为哈希函数

(2.5)查找之哈希查找_字符串


(2.5)查找之哈希查找_字符串_02

  • 哈希函数的特点
    (1)哈希函数是一个映象,即: 将关键字的集合映射到某个地址集合上, 它的设置很灵活,只要这个地址集合的大小不超出允许范围即可;
    (2) 由于哈希函数是一个压缩映象,因此,在一般情况下,很容易产生“冲突” 现象,即: key1≠key2,而 h(key1) = h(key2)。
  1. 很难找到一个不产生冲突的哈希函数。
    一般情况下, 只能选择恰当的哈希函数,使冲突尽可能少地产生。
    因此,哈希查找需要做两方面事情:选择一个“好”的哈希函数;提供一种“处理冲突” 的方法。
  • eg:

在学号范围内:XX000 ~ XX999,查找学号:17138,选择查找的方法
(1)顺序查找: O(n),平均约比较500次(平均比较的次数为:n/2)
(2)二分查找: O(logn),平均约比较10次
(3)哈希查找:取给定学号的后三位, 不需要经过比较, 便可直接从查找表中找到给定学生的记录。

2.哈希表

  • 根据设定的哈希函数 H(key) 和提供的处理冲突的方法,将一组关键字映象到一个地址连续的地址空间上,并以关键字在地址空间中的“象” 作为相应记录在表中的存储位置,如此构造所得的查找表称之为哈希表。
    地址空间存储的数据集合称为哈希表

3.好的哈希函数特点

  • (1)计算简单
  • (2)冲突少
  • 实际工作中需根据不同的情况采用不同的哈希函数。通常需要考虑的因素有:

计算哈希函数所需时间;
关键字的长度;
哈希表的大小;
关键字的分布情况;
记录的查找频率

4.常见的哈希函数的构造方法

  • 直接哈希函数

取关键字本身或关键字的某个线性函数值作为哈希地址,
即: H(key) =key
或 H(key) =a* key+b(a, b为常数)。

eg:key是出生年份

(2.5)查找之哈希查找_哈希查找_03

  • 数字分析法

设n个d位数的关键字,由r个不同的符号组成,此r个符号在关键字各位出现的频率不一定相同,可能在某些位上均匀分布,即每个符号出现的次数都接近于n/r次,而在另一些位上分布不均匀。则选择其中分布均匀的s位作为哈希地址,即H(key) =“key中数字均匀分布的s位”。

eg:

对于第一位和第八位而言,第一位只有8,第八位只有2,7,8不满足每个符号出现的次数接近于n/r

(2.5)查找之哈希查找_字符串_04

  • 平均取中法
    取关键字平方后的中间几位作为哈希地址,即哈希函数为:

H(key) =“key的平方的中间几位”,
其中,所取的位数由哈希表的大小确定

以关键字的平方值的中间几位作为存储地址。求“关键字的平方值” 的目的是“扩大差别”和“贡献均衡”。
即:关键字的各位都在平方值的中间几位有所贡献, Hash值中应该有各位影子。

eg:请为BASIC源程序中的标识符建立一个哈希表。假设BASIC语言中允许的标识符为一个字母,或一个字母加一个汉字。

取标识符在计算机中的八进制数为它的关键字。

(2.5)查找之哈希查找_哈希查找_05


(2.5)查找之哈希查找_均匀分布_06

  • 折叠法(若关键字位数特别多)
    (1)关键字位数较长时,可将关键字分割成位数相等的几部分(最后一部分位数可以不同),取这几部分的叠加和(舍去高位的进位)作为哈希地址。位数由存储地址的位数确定。
    (2)叠加时有两种方法:
    • 移位叠加法,即将每部分的最后一位对齐,然后相加;
    • 边界叠加法,即把关键字看作一纸条,从一端向另一端沿边界逐次折叠,后对齐相加。
    eg:此种方法适合于:关键字的数字位数特别多的情况
  • 除留余数法(若关键字位数特别多)
    取关键字被某个不大于哈希表长度m的数p除后的余数作为哈希地址,即:

H(key) =key MOD p(p≤m)
p通常要选择质数

eg:

(2.5)查找之哈希查找_均匀分布_07

  • 随机数法(若关键字位数特别多)
    选择一个随机函数,取关键字的随机函数值作为哈希地址,即:

H(key) =random(key)
其中random为随机函数。

6.字符串的哈希查找

  • eg:牛津字典的查找和编译器的符号表都是哈希查找

Example〗 In Oxford English dictionary
name = since
attribute = a list of meanings:M[0] = after a date, event, etc.
M[1] = seeing that (expressing reason)
…… ……

  • 对于哈希函数的要求
    (1) f ( x )必须能够计算任意关键字且冲突最少
    (2) f ( x ) 应该均匀分布

比如对任意 x和 i, 有如下概率:
Probability( f ( x ) = i ) = 1 / b. 这种函数函数叫(均匀分布哈希函数)
uniform hash function.

f ( x ) = x % TableSize ; /* if x is an integer */
说明:
因为:
如果TableSiz=10,如果x都是10的倍数的整数,其哈希值全都为0,会有大量的冲突
所以:
TableSize=prime number,即:TableSize的取值为选择小于TableSize的最大质数

  • 如何获取字符串的x?也就是说,如何获取字符串的key x?

由于每个字符的ASCII码本身就是整数,利用ASCII码进行求和来作为x,所以哈希函数为:
f ( x ) = (Σ x[i]) % TableSize ; /* if x is a string */

eg:TableSize = 10,007 and string length of x <= 8(字符串的长度).
If x[i] ∈ [0, 127](每个字符的ASCII只能是0-127), then f(x)∈ [0, 1016]
(哈希值的结果最多是:128*8=1016种情况)
因为这种情况哈希值的范围最多为1016,远远小于10007,所以需要扩大key值。

扩大key的方法是:26个字符+空格=27个,取前三位
f(x) = (x[0]+ x[1]*27 + x[2]*27^2) % TableSize;
相当于:*27^0 *27^1 *27^2

因为:
128*27^2已经超过10007了,避免了前面只有f(x)只有1016的大小,但是所有字符串的前3个字符的组合
是少于3000个的,
所以:
只取部分位又会出现重复,此外,求幂运算的计算量很大

结论:
要让所有的位都参与到哈希函数的计算当中,高次幂转换成移位操作,这样计算速度就很快,公式如下(乘以32相当于左移5位):
说明:
(1)若字符串很长,可以让整个字符串参与整体移位,需要自己写移位操作函数
(2)若字符串很长,可以选择某些位来参与运算

(2)的代码为:
//x是字符串
Index Hash3(const char *x, int TableSize)
{
unsigned int HsahVal=0;
while(*x!='\0')
{
//如果x太长,选择x当中的某些字符来实现哈希操作,以防止左移出空间之外
HashVal=(HashVal<<5)+(*x)++;
}
return HashVal%TableSize;
}

(2.5)查找之哈希查找_均匀分布_08


举报

相关推荐

0 条评论