数据结构与算法分析 —— C 语言描述:散列函数

对散列函数而言,如果输入的关键字是整数,则一般合理的方法就是直接返回“Key mod TableSize” 的结果,除非 Key 碰巧具有某些不理想的性质。在这种情况下,散列函数的选择需要仔细考虑,例如,若表的大小是 10 而关键字都以 0 为个位,则此时上述标准的散列函数就是一个不好的选择。其原因我们将在后面讨论,而为了避免上述情况,好的办法通常是保证表的大小事素数。当输入的关键字是随机整数时,散列函数不仅算起来简单而且关键字的分配也很均匀。

通常,关键字是字符串;在这种情况下,散列函数需要仔细地选择。

一种选择办法是把字符串中字符的 ASCII 码值加起来。例如,设 TableSize = 10007(10007 是素数),并设所有的关键字至多 8 个字符长。由于 char 型量的值最多是 127,因此散列函数只能假设值在 0 和 1016 之间,其中 1016 = 127 * 8。显然,这不是一种均匀的分配。

另一个散列函数如下:

Index
Hash( const char *key, int TableSize)
{
    return ( Key[0] + 27 * Key[1] + 729 * Key[2]) % TableSize;
}

这个散列函数假设 Key 至少有两个字符外加 NULL 结束符。值 27 表示英文字母表的字母个数外加一个空格,而 729 = 27^2。该函数只考察前三个字符,但是,假如它们是随机的,而表的大小像前面那样还是 10007,那么我们就会得到一个合理的均衡分配。可不巧的是,英文不是随机的。虽然三个字符(忽略空格)有 26^3 = 17576 种可能的组合,但查验词汇量足够大的联机辞典却揭示: 3个字母的不同组合数只有 2851 种。即使这些组合没有冲突,也不过只有表的 28\%被真正散列到。因此,虽然很容易计算,但是当散列表足够大的时候这个函数还是不合适的。

第三种散列函数如下:

Index
Hash(const char *key, int TableSize)
{
    unsigned int HashVal = 0;
    while ( *key != '\0')
        HashVal = ( HashVal << 5) + *Key++;

    return HashVal % TableSize;
}

这个散列函数涉及关键字种所有的字符,并且一般可以分布得很好(它计算 \sum_{i\;=\;0}^{keySize\;-\;1}\;Key\lbrack KeySize\;-\;i\;-\;1\rbrack\;\cdot\;32^i,并将结果限制在适当的范围内)。程序根据 Horner 法则计算一个(32 的)多项式函数。例如,计算 h_k = k_1 + 27k_2 + {27}^2K_3的另一种方式是借助公式 h_k = ((k_3)*27+k_2)*27+k_1进行。Horner 法则将其扩展到用于 n 次多项式。

我们之所以用 32 代替 27,是因为用 32 坐乘法不是真的去乘,而是移动二进制的 5 位,为了加速,在程序的加法可以用按位异或来代替。

上面描述的散列函数就表的分布而言未必是最好的,但是确实具有极其简单的优点(如果允许溢出,那么速度也很快)。如果关键字特别长,那么该散列函数计算起来将会花费过多的时间,不仅如此,前面的字符还会左移出最终的结果。在这种情况下,通常做的做法是不使用所有的字符。此时关键字的长度和性质将影响选择。例如,关键字可能是完整的街道地址,散列函数可以包括街道地址的几个字符,也许是城市名和邮政区码的几个字符。有些程序设计人员通过只使用奇数位置上的字符来实现他们的散列函数,这里有这么一层想法:用计算散列函数节省下的时间来补偿由此产生的对均匀分布的函数的轻微干扰。

剩下的主要变成细节是解决冲突的消除问题。如果当一个元素被插入处另一个元素已经存在(散列值相同),那么就产生一个冲突,这个冲突需要消除。解决这种冲突的方法有几种,其中最简单的两种分别为:分离链接法和开发地址法。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,496评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,407评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,632评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,180评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,198评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,165评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,052评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,910评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,324评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,542评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,711评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,424评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,017评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,668评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,823评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,722评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,611评论 2 353

推荐阅读更多精彩内容