(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)
}