目录
一、优秀的哈希函数
在构造哈希表之前,我们需要一个哈希函数对数据进行哈希化。
那这个哈希函数怎么实现呢,根据前面一篇博客【JS数据结构】认识哈希表,我们已经认识了什么是哈希表以及为什么需要设计一个哈希函数。
其实就是要达到两个目的:
- 能够快速地计算,快速地获取hashCode
- 让元素在哈希表中分布均匀。
1、幂的连乘
前面谈到的数据存储,一种方法是使用幂的连乘获取hashCode
give = 7 * 27^3 + 9 * 27^2 + 22 * 27 + 5 = 144941
采用这种方式,其实就是一个多项式,可化为:
这里相乘的次数为:n + (n - 1) + ... + 1 = n(n + 1) / 2次
相加的次数为: N次。
得到的时间复杂度为(N^2 + N)/ 2 即O(N^2)
2、霍纳法则(秦九韶算法)
霍纳法则是对多项式的优化,让相乘的次数变少,从而快速获取hashCode,化为以下形式:
以give为例
这里相乘的次数为:N次;
相加的次数为: N次。
得到的时间复杂度为(N + N) 即O(N)
所以使用霍纳法则可以大大地提高效率,减少计算时间。
3、如何做到均匀分布。
在设计哈希表时,我们已经有了两种处理映射到相同下标值的办法,即可以解决冲突,一种是链地址法,另一种是开放地址法。
不管是哪种方法,我们最好让数据在哈希表中分布均匀。
所以我们在使用常量的时候,要使用质数
1、哈希表的长度。
2、N次幂的底数(常使用37)。
质数很重要,假设表长为10(下标值为0~9)
一个特定的关键字映射到下标值为0的位置,步长为5,那么探索的序列就会是0-5-0-5...一直循环下去。
如果表长为11,那么探索的序列为:0-5-10-4-9-3-8-2-7-1-6,这样的话就不会产生循环并且可以让数据在哈希表中分布均匀。
二、实现哈希函数
设计一个哈希函数
1、将字符串转换成一个比较大的数字,即得hashCode。
2、将这个大的数字hashCode压缩成数组大小的范围之内。
需要传入两个参数,一个是字符串,一个是哈希表的长度。
function hashFunc(str, size){
//定义一个变量来存储hashCode
var hashCode = 0;
// 利用霍纳法则计算出hashCode的值
// give -> Unicode编码
for (var i = 0; i < str.length; i++) {
hashCode = 37 * hashCode + str.charCodeAt(i);
}
// 利用hashCode与哈希表的长度取余得到下标值
var index = hashCode % size;
return index;
}
测试代码:
//测试
alert(hashFunc('abc', 7)); // 4
alert(hashFunc('cla', 7)); // 2
alert(hashFunc('nbr', 7)); // 1
alert(hashFunc('kba', 7)); // 0
总结:优秀的哈希函数是尽可能地快速将数据映射到不同的位置上。