目录
数字关键词的散列函数构造
直接定址法
除留余数法
数字分析法
折叠法
平方取中法
字符关键词的散列函数构造
ASCII码加和法
前三字符移位法
移位法
数字关键词的散列函数构造
一个“好”的散列函数一般应考虑下列两个因素:
- 计算简单,以便提高转换速度;
- 关键词对应的地址空间分布均匀,以尽量减少冲突。
直接定址法
取关键词的某个线性函数值为散列地址,即(a、b为常数)。
例如以年份(1990-2011)为关键词,那么就可以将散列函数构造成
地址h(key) | 出生年份(key) | 人数(attribute) |
---|---|---|
0 | 1990 | 1285万 |
1 | 1991 | 1281万 |
2 | 1992 | 1280万 |
...... | ...... | ...... |
10 | 2000 | 1250万 |
...... | ...... | ...... |
21 | 2011 | 1180万 |
除留余数法
散列函数为:
例如上篇里提到的:
地址 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
关键词 | 34 | 18 | 2 | 20 | 23 | 7 | 42 | 27 | 11 | 30 | 15 |
这里的散列函数就为:h(key) = key % 17
- 此处p = TableSize = 17
- 一般地,p取素数
选择素数p可以避免余数之间的相关性,降低哈希冲突的概率。如果选择合数作为模数,可能存在公因数,增加了冲突的概率。
数字分析法
分析数字关键字在各位上的变化情况,取比较随机的位作为散列地址。
比如:取11位手机号码key的后4位作为地址,则散列函数为:(char *key)
即,手机号码为12345678910,其地址就为“8910”。
例子
关键词key是18位的身份证号码:
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
3 | 3 | 0 | 1 | 0 | 6 | 1 | 9 | 9 | 0 | 1 | 0 | 0 | 8 | 0 | 4 | 1 | 9 |
省 | 市 | 区(县)下 属辖区编号 | (出生)年份 | 月份 | 日期 | 该辖区中的序号 | 校验 |
根据分析,发现变化比较大的就是我们标红的六位,所以我们以他们六位来设计散列函数:
当key[18] = ‘X’时,
当key[18]为‘0’~‘9’时,
(以十进制来计算地址,所以最高位为10的5次方)
折叠法
把关键词分隔成位数相同的几个部分,然后叠加。
比如:56793542,拆分成三部分
,取后三位391,所以h(56793542) = 391
平方取中法
将一个数平方之后再取中间位数的值。
比如:56793542
所以h(56793542)=641
字符关键词的散列函数构造
ASCII码加和法
对字符型关键词key定义散列函数如下:
但是这种方法冲突比较严重,a3(97+3)、b2(98+2)、c1(99+1)的ASCII码值加起来都等于100,eat(101+97+116)和tea(116+101+97)的ASCII码值加起来都等于314.
进行一下简单的改进
前三字符移位法
(这里用27进制计算地址,原因是包括空格在内的单个字符共有27个)
但是这种方法仍然容易冲突:string、street、strong、structure等等;
而且会造成空间浪费,理论上来说,前三字符所有可能性的组合为种,但经过统计得到,实际中的组合约3000种,,即空间利用率大约30%,浪费了70%的空间。
移位法
涉及关键词所有n个字符,并且分布得很好;
例如:h(“abcde”) = ‘a’ * + ‘b’ * + ‘c’ * + ‘d’ * 32 + ‘e’。
用这个计算方法需要乘10次,我们看一下比较快的计算方法:
h(“abcde”) = (((‘a’ * 32 + b) * 32 + c) * 32 + d ) * 32 + e,
用这种方法,在计算时就只需要乘4次。
还有没有更巧妙的办法呢?
我们发现32是2的5次方,于是可以通过移位,也就是将一个数往左二进制移位五次,就相当于*32.
于是有:
Index Hash(const char *Key, int TableSize)
{unsigned int h = 0; /* 散列函数值,初始化为0 */while( *Key != '\0'){h = (h << 5) + *Key++;}return h % TableSize;
}
end
学习自:MOOC数据结构——陈越、何钦铭