散列表的实现通常叫做散列。散列是一种用于以常数平均时间执行插入、删除和查找的技术。但是任何排序的信息都不会得到有效的支持。所以FindMax(),FindMin(),以及以线性时间打印的操作都是散列所不支持的。
理想的散列表数据结构值不过是一个包含有关键字的具有固定大小的数组。
关键字映射的函数叫做散列函数,通常散列函数应该运算简单并且保证任何两个不同的关键字映射到不同的单元。不过这是不可能的,因为单元的数目是有限的,然而关键字是用不完的。因此,我们寻找一个散列函数,该函数要在单元之间均匀的分配的关键字。对于两个关键字映射到同一个值的时候,我们称之为冲突,需要设定一个函数来进行处理。
散列函数
对于关键字是整数,则一般合理的方法就是直接返回"Key mod TableSize"的结果,除非Key具有某些不理想的性质。例如:表的大小是10,但是关键字的大小都是0为个位。好的大小通常是保证表的大小是一个素数。
通常,关键字是字符串,在这种情况下,散列函数需要仔细的选择。
一种比较简单的方法是把字符串中的字符的ASCLL码值加起来。下面是这种方式的代码实现:
Indexx Hash(const char *Key, int TableSize){ unsigned int HashVal = 0; while(*Key != '\0'){ HashVal += *Key++; } return HashVal % TableSize; }
上述的散列函数实现起来简单而且很快地算出答案。不过,如果表很大的话,函数将不会很好的分配关键字。假设TableSize=10007,并且假设所有的关键字最多有8个字符长,127*8=1016,显然这是不均匀的分配。
另一种散列函数有下面的代码所示,假设关键字key至少有两个字符加上NULL结束,729=27^2
假设它们是随机的,而表还是10007的大小,我们就会得到一个合理的均匀分配,虽然3个字符有26^3=17576种可能的组合,但是实际的词汇量却揭示了:3和字母不同的组合数实际上面只有2851种,也只不过有28%的空间被利用上。当表足够大的时候,它们还是不合适的
Index Hash(const char *Key, int TableSize){ return (Key[0] + 27 * Key[1] + 729 * Key[2]) % TableSize; }
下面的散列函数,涉及到关键字中的所有字符,并且一般可以分布的很好,程序根据Horner法则计算一个(32的)多项式。
Index Hash(const char *Key, int TableSize){ unsigned int HashVal=0; while(*Key != '\0'){ HashVal = (HashVal<<5) + *Key++; } return HashVal % TableSize; }
之所以使用32是因为可以使用位运算来加速,并且还可以使用按位异或来代替。上述的散列函数的优点是简单且允许溢出。当关键字长的时候,可以选用部分的关键字。有些程序人员通过只使用奇数位置上的字符来实现他们的散列函数。这