hash函数
//哈希
class Hash {
constructor(str, size) {
if (
typeof str !== "string" || typeof size !== "number" ||
str.trim().length === 0 || size <= 0 || size % 1 > 0
) {
throw new Error("Hash class的初始化参数不合规范!");
}
this.str = str;
this.size = size;
this.getHashCode();
}
//计算hashCode,扩大映射的那个值
getHashCode() {
const {str, size} = this;
//需要字典:默认unicode
//需要性能,秦九韶算法:提取公因式,循环累乘:
let hashCode = 0;
for (let i =0;i<str.length;i++){
//幂运算的底数,最好是质数:套路论:37用的多。
//这里有个疑问:str的空格,我到底要不要删掉?我没删,主动权给用户了!
hashCode = hashCode * 37 + str.charCodeAt(i);
}
this.hashCode = hashCode % size;
}
}
const hash1 = new Hash("abc", 101);
const hash2 = new Hash("bcd ", 101);
const hash3 = new Hash("cba", 101);
console.log(hash1, hash2, hash3);
哈希表的添加和修改
class HashTable {
constructor(size) {
this.table = [];
this.count = 0; //存了多少个。
// 跟size对比,装载因子 >0.75,性能->扩容。
// 或<0.25时,内存大,count小,就减小数组的长度。自优化!
this.limit = size; //table长度
}
//存储的数据是{kye,...data},key是唯一标识字段,可以得出index的根据,
// data,是要存入的数据
put(obj) {
if (!obj || typeof obj !== "object" || !obj.key) {
throw new Error("要存入的数据,必须要是个带有key字段属性的object!");
}
const i = new Hash(obj.key, this.limit).hashCode;
const current = this.table[i];
if (current) {
//这里很纠结的,首先,我解耦了要存的数据是个对象这种复杂类型,并且我没用数组
//链表存储时候,链表的查询方法indexOf那里,对比的是DoubleList里的data
//所以这里的对比就很真实对比了,要么对比简单数据的值,要么对比复杂对象的内存地址
//如果数据本身拷贝过,那就完了!而且data的值就是obj这个参数的值!
//这里就会限定死了如何匹配查找时,要传入的匹配参照就必须是原本的obj,而不是key
//当然,我确实需要扩展一下 LinkedList的indexOf方法,更健壮。
//当初封装的时候,我一直都有这方面的疑惑的,为啥不是凭借关键字段来匹配?
//后来觉得,没必要的,尽可能地简单字段。同时就算是复杂的数据的内存地址,那就全等于喽!
if (current.key === obj.key) { //有key,就不是doublelinedlist class
//相等代表,修改操作!
this.table[i] = obj;
} else if (current.key) { //不相等,说明重复了!新建链表!
const doublyLinkedList = new DoublyLinkedList(
new DoublyList(current));
doublyLinkedList.append(obj);
this.table[i] = doublyLinkedList;
this.count += 1;
} else { //没有key,说明是链表了
//说的就是这里:匹配的是key! 我改了indexOf的方法!为了更好用!
const index = current.indexOf(obj.key, "key");
if (index < 0) {
current.append(obj);
this.count += 1;
} else {
current.update(index, obj);
}
}
} else {
//常规添加:
this.table[i] = obj;
this.count += 1;
}
}
}
const hash_table = new HashTable(7)
hash_table.put(
{
key:'abc',name:'liuliu',age:24
}
)
hash_table.put(
{
key:'abc',name:'liuliu',age:22
}
)
console.log(hash_table.table,hash_table.count);
setTimeout(
()=>{
hash_table.put(
{
key:'bcde',name:'liuliu',age:23
}
)
console.log('tt',hash_table.table,hash_table.count);
}
)
通过key拿到数据:
get(key) {
const index = new Hash(key, this.limit).hashCode;
const current = this.table[index];
if (!current) return null;
if (current.key) return current;
const ind = current.indexOf(key, "key"); //linkedlist class扩展后的indexOf方法
if (ind < 0) return null;
return current.get(ind).data;
}
删除:
remove(key) {
const index = new Hash(key, this.limit).hashCode;
const current = this.table[index];
if (current === undefined || current === null) return null;
if (current.key) {
this.table[index] = null;
return current;
};
//这里就是doubleLinkedList的实例了:
const ind = current.indexOf(key, "key");
if (ind < 0) return null;
this.count -= 1;
return current.removeAt(ind);
//到了这里,我突然就觉得indexOf方法的效率低了,indexOf不应该只是返回index,还要返回当前的list。如此
通过indexOf拿到的index,或者list节点,在removeAt上就太容易了!不需要通过index再 遍历确定list节点了。
}