哈希表存储元素时将key进行hash计算,hash值转成索引后再存储元素
使用链地址法解决哈希冲突问题,如图所示哈希表每一项中再存储一个数组或链表用于存储hash值相同的元素
随着元素的插入和删除,需要对哈希表进行扩容或缩容操作
function HashTable() {
//容器
this.storage = []
//大小
this.count = 0
//容量
this.limit = 7
}
hashFunc(str, size) {
let hashCode = 0
for(let i = 0; i < str.length; i++) {
hashCode = 37 * hashCode + str.charCodeAt(i)
}
let index = hashCode % size
return index
}
put(key, value) {
//获取key对应的索引
let index = hashFunc(key, this.limit)
//获取对应的bucket
let bucket = this.storage[index]
//bucket不存在
if(bucket === undefined) {
//创建空数组放到对应的索引中
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 += 1
//判断是否需要扩容
if(this.count > this.limit * 0.75) {
//获取新的容量 当前容量的2倍
let newSize = this.limit * 2
//保持当前容量是素数
let newLimit = this.getPrime(newSize)
this.resize(newLimit)
}
}
get(key) {
//获取索引
let index = hashFunc(key, this.limit)
//获取索引对应的元素或数组或链表
let bucket = this.storage[index]
if(bucket === undefined) {
return null
}
//遍历
for(let i = 0; i < bucket.length; i++) {
let tuple = bucket[i]
if(tuple[0] === key) {
return tuple[1]
}
}
//没找到
return null
}
remove(key) {
//获取索引
let index = hashFunc(key, this.limit)
//获取索引对应的元素或数组或链表
let bucket = this.storage[index]
if(bucket === undefined) {
return null
}
//遍历
for(let i = 0; i < bucket.length; i++) {
let tuple = bucket[i]
if(tuple[0] === key) {
bucket.splice(i, 1)
this.count -= 1
//判断是否需要缩容
if(this.limit > 7 && this.count < this.limit * 0.25) {
//获取新的容量 是当前容量的一半向下取整
let newSize = Math.floor(this.limit / 2)
//保持新的容量是素数
let newLimit = this.getPrime(newSize)
this.resize(newLimit)
}
return tuple[1]
}
}
return null
}
isEmpty() {
return this.count === 0
}
size() {
return this.count
}
resize(newLimit) {
//指向老容器的引用
let oldStorage = this.storage
//清空当前容器 准备扩容
this.storage = []
this.count = 0
//新容量
this.limit = newLimit
//遍历老容器
for(let i = 0; i < oldStorage.length; i++) {
//获取容器每一项
let bucket = oldStorage[i]
if(bucket === undefined) {
//为空终止本次循环 执行下一次
continue
}
//遍历 老元素添加到新容器中
for(let j = 0; j < bucket.length; j++) {
let tuple = bucket[j]
this.put(tuple[0], tuple[1])
}
}
}
isPrime(num) {
let temp = parseInt(Math.sqrt(num))
for(let i = 0; i < temp; i++) {
if(num % i === 0) {
return false
}
}
return true
}
getPrime(num) {
while(!this.isPrime(num)) {
num++
}
return num
}