1 定义
散列技术是在记录的存储位置和它的关键位置之间建立一个确定的对应关系f
,使得每个关键字key
对应一个存储位置f(key)
,即:
存储位置 = f(关键字)
我们把对应关系f
称为散列函数(Hash函数),采用散列技术将记录存储在一块连续的存储空间中,这块连续的存储空间称为散列表或hash表
2 常见的散列函数
选取好的散列函数的原则:计算简单、散列地址分布均匀
- 直接定址法:
f(key)= a*key + b (a,b为常数)
,适用于知道关键字分布,查找表较小且连续的情况 - 数字分析法: 抽取关键字的一部分数字来计算散列存储位置,适用于关键字位数较多的情况
- 平方取中法: 将关键字平方后抽取中间几位数字作为散列地址,适用于不知道关键字的分布,而位数又不是很大的情况
- 折叠法: 将关键字从左至右分割成位数相等的几部分,并将这几部分叠加求和,取后几位作为散列地址。适用于不知道关键字分布,且位数较多的情况。
- 除留余数法:
f(key) = key mod p (p<=m)
,对关键字直接取模作为散列地址,是最常用的构造散列函数方法 - 随机数法:
f(key) = random(key)
,选取一个随机数,取关键字的随机函数值为它的散列地址。适用于关键字长度不等的情况。
3 处理散列冲突
- 开放地址法:一旦发生地址冲突,就去寻找下一个空的散列地址。
线性探测法
fi(key) = (f(key)+ di)MOD m (di = 1,2,3,…,m-1)
二次探测法
fi(key) = (f(key)+ di)MOD m (di = 12,-12,22,-22,…,q2,-q2,q<=m/2)
随机探测法
fi(key) = (f(key)+ di)MOD m (di 是一个随机数列) - 再散列函数法:对于我们的散列表,事先准备多个散列函数,发生冲突时,就换一个散列函数计算
- 链地址法:发生冲突时,将关键字为同义词的记录存储在一个单链表中(同义词字表),在散列表中只存储所有同义词子表的头指针,在查找时,可能需要遍历单链表
- 公共溢出区法:将所有冲突的关键词都统一存储到溢出表中,查找时,先与散列表中的值做比较,若不等再去溢出表中遍历查找。
4 示例
const maxLength = 10; //hash表的长度
let hashTable = [];
hashTable.len = 0;
hashTable.getAddr = function(key){ //计算hash地址
return key % maxLength;
};
hashTable.add = function(key){ //插入关键字
if (this.len >= maxLength){
console.log("存储失败,空间不足");
return false;
}
let addr = this.getAddr(key);
while(this[addr] !== undefined){ //开放地址法的线性探测解决地址冲突
addr = (addr + 1) % maxLength;
}
this[addr] = key;
this.len++;
console.log("存储成功:"+key);
return true;
};
hashTable.get = function(key){ //查找关键字地址
let addr = this.getAddr(key);
while(this[addr]!==key){
addr = (addr + 1) % maxLength;
if (addr === this.getAddr(key)){
console.log(key+"不存在");
return -1;
}
}
console.log(key+"的存储位置为:"+addr);
return addr;
}