散列(哈希)
散列也被称为哈希。散列后的数据可以快速存取。散列使用的数据结构是散列表。插入、删除和取用速度快,但是查找速度慢。
通过散列表存储数据时,通过散列函数将键映射为一个数字,数字的范围是0到散列表的长度。
当两个键映射为一个值的情况叫作碰撞。
散列表的数组长度应该是一个质数。
选择散列函数
散列函数的选择取决于键值的数据类型。除留余数法。如果键的类型是字符串将各个字符的ASCII的值的和除以数组的长度的余数。
hashTable.prototype.hashCode = function (str, size) {
let hashCode = 0;
// 霍纳算法计算hashCode
for (let i = 0; i < str.length; i++) {
hashCode = 37 * hashCode + str.charCodeAt(i);
}
index = hashCode % size;
return index;
}
碰撞处理
开链法
实现散列表的底层数组中,每个元素又是另一个数据结构。在创建存储散列过的键值的数组时,通过调用一个函数创建一个新的空数组,然后将该数组赋给散列表里的每个数组元素。这样就创建了一个二维数组
hashTable.prototype.put = function (key, value) {
// 根据key获取索引位置
// 根据索引取出bucket
// 判断新增还是修改原来的值
let index = this.hashCode(key, this.limit);
let bucket = this.storage[index];
if (bucket == null) {
bucket = [];
this.storage[index] = bucket;
}
for (let i = 0; i < bucket.length; i++) {
let tuple = bucket[i];
if (tuple[0] === key) {
tuple[1] = value;
return;
}
}
bucket.push([key, value]);
this.count++;
if (this.count > this.limit * 0.75) {
this.resize(this.getPrime(this.limit * 2));
}
}
线性检测法
线性探测法隶属于一种更一般化的散列技术:开放寻址散列。当遇到碰撞时,检测散列表的下一个的位置是否为空,如果为空则将数据存入该位置,如果不为空则继续检测下一个位置,直到找到一个空的位置为止。当存储数据的数组特别大时,使用线性检测法比开链法好。
如果数组的大小是待存储数据个数的1.5倍时,使用开链法;如果数组的大小是待存储数据个数的2倍及以上使用线性检测法。
hashTable.prototype.lineDetectPut = function (key, value){
let pos = this.hashCode(key);
if(this.table[pos] === undefined){
this.table[pos] = key;
this.values[pos] = data;
} else {
while(this.table[pos] !== undefined){
pos++;
}
this.table[pos] = key;
this.values[pos] = value;
}
}