javascript數據結構與算法----哈希表擴容

(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)
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章