表长m最好是个质数,再哈希函数最好是key%(m-某数)+1 ,否则小心增量过程一直循环.
字符串的Hash函数
字符串本身就可以看成一个256进制(ANSI字符串为128进制)的大整数,因此我们可以利用直接取余法,在线性时间内直接算出Hash函数值。为了保证效果, 仍然不能选择太接近 的数;尤其是当我们把字符串看成一个 进制数的时候,选择 会使得该字符串的任意一个排列的Hash函数值都相同。(想想看,为什么?)
常用的字符串Hash函数还有ELFHash,APHash等等,都是十分简单有效的方法。这些函数使用位运算使得每一个字符都对最后的函数值产生影响。另外还有以MD5和SHA1为代表的杂凑函数,这些函数几乎不可能找到碰撞(MD5前一段时间才刚刚被破解)。
我从Mark Twain的一篇小说中分别随机抽取了1000个不同的单词和1000个不同的句子,作为短字符串和长字符串的测试数据,然后用不同的Hash函数把它们变成整数,再用直接取余法插入一个容量为1237的Hash表,遇到冲突则用新字符串覆盖旧字符串。通过观察最后“剩下”的字符串的个数,我们可以粗略地得出不同的Hash函数实际效果。
| 短字符串 | 长字符串 | 平均 | 编码难度 |
直接取余数 | 667 | 676 | 671.5 | 易 |
P. J. Weinberger Hash | 683 | 676 | 679.5 | 难 |
ELF Hash | 683 | 676 | 679.5 | 较难 |
SDBM Hash | 694 | 680 | 687.0 | 易 |
BKDR Hash | 665 | 710 | 687.5 | 较易 |
DJB Hash | 694 | 683 | 688.5 | 较易 |
AP Hash | 684 | 698 | 691.0 | 较难 |
RS Hash | 691 | 693 | 692.0 | 较难 |
JS Hash | 684 | 708 | 696.0 | 较易 |
把1000个随机数用直接取余法插入容量为1237的Hash表,其覆盖单元数也只达到了694,可见后面的几种方法已经达到了极限,随机性相当优秀。然而我们却很难选择,因为不存在完美的、既简单又实用的解决方案。我一般选择JS Hash或SDBM Hash作为字符串的Hash函数。这两个函数的代码如下:
unsigned int JSHash(char *str) { unsigned int hash = 1315423911; // nearly a prime - 1315423911 = 3 * 438474637 while (*str) { hash ^= ((hash << 5) + (*str++) + (hash >> 2)); } return (hash & 0x7FFFFFFF); } unsigned int SDBMHash(char *str) { unsigned int hash = 0; while (*str) { // equivalent to: hash = 65599*hash + (*str++); hash = (*str++) + (hash << 6) + (hash << 16) - hash; } return (hash & 0x7FFFFFFF); } |