散列(Hash)

您所在的位置:网站首页 散列表的实现 散列(Hash)

散列(Hash)

2023-09-28 03:06| 来源: 网络整理| 查看: 265

散列表的实现常称为散列(hashing)。散列是一种用于以常数平均时间执行插入、删除和查找的技术。但是,那些需要元素间任何排序信息的操作将不会得到有效的支持。因此,诸如findMin、findMax以及在线性时间内按顺序打印整个表的操作都是散列表所不支持的。

基本概念

理想的散列表数据结构只不过是一个包含一些项的具有固定大小的数组。 查找一般是对项的某个部分(即数据成员)进行,这部分称为键(key)。例如,项可以由字符串(它可以作为键)和附加的数据成员组成。我们把表的大小记作TableSize,并将其理解为散列数据结构的一部分,而不仅仅是服务于全局的某个变量。通常的习惯是让表从0到TableSize- 1变化;稍后我们就会明白为什么要这样。 在这里插入图片描述

将每个键映射到从0到TableSize-1这个范围中的某个数,并且将其放到适当的单元中。这个映射就称为散列函数(hash function),理想情况下它应该运算简单并且应该保证任何两个不同的键映射到不同的单元。不过,这是不可能的,因为单元的数目是有限的,而键实际上是用不完的。因此,我们寻找一个散列函数,该函数要在单元之间均匀地分配键。这就是散列的基本思想。剩下的问题则是要选择一个函数,决定当两个键散列到同一个值的时候(称为冲突(collision))应该做什么以及如何确定散列表的大小。

散列函数

如果输入的键是整数,则一般合理的方法就是直接返回 Key mod Tablesize,除非Key碰巧具有某些不理想的性质。在这种情况下,散列函数的选择需要仔细考虑。例如,若表的大小是10而键的个位都是0,则此时上述标准的散列函数就是一个不好的选择。其原因我们将在后面介绍,而为了避免上面那样的情况,好的办法通常是保证表的大小是素数。当输入的键是随机整数时,散列函数不仅运算简单而且键的分配也很均匀。通常,键是字符串;在这种情形下,散列函数需要仔细选择。 一种选择方法是把字符串中字符的ASCII码值加起来。

int hash(const string &key,int tablesize) { int hashVal = 0; for(auto c : key) hashVal += c; return hashVal % tablesize; }

不过,如果表很大,则函数就不会很好地分配键。例如,设TableSize = 10007 (10007是素数),并设所有的键至多8个字符长。由于ASCII字符的值最多是127,因此散列函数只能在0~1016之间取值,其中1016为127×8。显然这不是一种均匀的分配。

另一个散列函数如下所示。

int hash(const string & key ,int tablesize ) { return (key[0] + 27*key[1] + 729*key[2]) % tablesize; }

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

还有第三种散列函数的尝试。

int hash(const string & key , int tablesize ) { int hashVal = 0; for(auto c : key) hashVal = 37*hashVal + c; hashVal %= tablesize; if


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3