(1)为什么需要扩容?
因为常用的链地址法,loadFactor可以大于1,所以此时设计的哈希表可以无限的插入数据,但是随着数据量的增加,每一个index对应的bucket会越来越长,也就是造成了效率的降低。所以在适当的情况下需要对数组进行扩容,比如2倍。
(2)如何进行扩容?
可以简单的将之前的容量扩大两倍,但是在这种情况下,所有的数据项一定要同事修改(重新调用哈希函数,获取不同的位置),比如hashCode为12的数据项的时候,在length为8的时候,index=5,在长度为16的时候,index=12。这是一个耗时的过程,但是如果数组需要扩容,那么这个过程是必要的。
(3)什么情况进行扩容?
当loadFactor>0.75的时候,比如加吧的哈希表家室在填充因子大于0.75的时候就对哈希表进行扩容。
(4)哈希表扩容的代码实现
HashTable.prototype.resize=function(newlimit){
// 先保存就得数据
var oldStorage = this.storage
// 重置属性
this.storage = []
this.count = 0
this.limit = newlimit
// 遍历
for(var i =0;i<oldStorage.length;i++){
var bucket = oldStorage[i]
if(!bucket){
continue
}else{
for(var j=0;j<bucket.length;j++){
var tuple = bucket[i]
this.put(tuple[0],tuple[1])
}
}
}
}
在元素进行插入的时候,需要进行判断是否需要进行扩容操作
if(this.count > this.limit * 0.75){
this.resize(this.limit * 2)
}
// 优化原本的put的实现
HashTable.prototype.put=function(key,value){
// 1.根据key获取index
var index = this.hashFunc(key,this.limit)
// 根据index取出对应的桶
var bucket = this.storage[index]
// 如果对应的位置没有桶那么就进行新创建
if(!bucket){
bucket = []
this.storage[index] = bucket
}
// 判断是否是修改数据
for(var i=0;i<bucket.length;i++){
var tuple = bucket[i] //此时bucket的每一个元素也是数组的形式
if(tuple[0] == key){
tuple[1] = value
return
}
}
// 进行添加
bucket.push([key,value])
this.count += 1
if(this.count > this.limit * 0.75){
this.resize(this.limit * 2)
}
}
(4)哈希表缩容,在删除操作的时候,
if(this.limit >7 && this.count < this.limit * 0.25 ){
this.resize(Math.floor(this.limit/2))
}
优化原本的删除操作
HashTable.prototype.remove=function(key){
var index = this.hashFunc(key,this.limit)
var bucket = this.storage[index]
if(!bucket){
return null
}else{
for(var i=0;i<bucket.length;i++){
var tuple = bucket[i]
if(tuple[0] == key){
// 从bucket数组中删除当前元素i
bucket.splice(i,1)
// 总数减少
this.count--
// 缩容
if(this.limit >7 && this.count < this.limit * 0.25 ){
this.resize(Math.floor(this.limit/2))
}
// 返回当前删除的元素
return tuple[1]
}
}
return null
}
}
对扩容进行优化,判断扩容之后是不是一个质数,如果不是质数的话,就需要寻找一个接近于2 被的质数,将这个新的质数作为新的容量。
面试题:如何判断一个数是不是质数?
第一种实现:通过循环,但是效率不是很高。
function judeNum(num){
if(typeof num != 'number'){
return '请输入一个数字'
}else{
// 循环输出
for(var i=2;i<num;i++){
if(num % i == 0) return `${num}不是质数`
}
return `${num}是质数`
}
}
第二种实现:
如果一个数字能够被分解,那么它分解的因子一个会大于等于它本身的开平方根,一个小于等于它本身的开平方根。
function judeNum2(num){
if(typeof num != 'number'){
return '请输入一个数字'
}else{
var temp = parseInt(Math.sqrt(num))
for(var i=2;i<=temp;i++){
if(num % i == 0) return `${num}不是质数`
}
return `${num}是质数`
}
}
如何岁哈希表的扩容进行优化?
// 判断当前传入数字是否是质数
HashTable.prototype.isPrime=function(num){
var temp = parseInt(Math.sqrt(num))
for(var i=2;i<=temp;i++){
if(num % i == 0) return false
}
return true
}
// 获取质数的方法
HashTable.prototype.getPrime=function(num){
while(!this.isPrime(num)){
num ++
}
return num
}
getPrime函数的调用是在改变数组容量的地方,即是元素添加和操作删除的地方,优化原本的代码为
if(this.count > this.limit * 0.75){
var newSize = this.getPrime(this.limit * 2)
this.resize(newSize)
}