JS哈希表的實現


前言:

數組進行插入的時候,效率比較低
數組進行查找的時候

1.如果基於下標 則很快O(1)
2.如果基於內容去查找,則很低

數組進行刪除的時候,效率低
數組進行修改的時候,
1.如果基於下標 則很快O(1)
2.如果基於內容,則很低

哈希表通常是基於數組實現的

優勢:
插入-刪除-查找 巨快
查找的速度比樹還要快
編碼比數簡單

劣勢:
key不能重復,數值存放是無序的。

實現的關鍵地方:
1.數組下標值的轉換。通過哈希函數實現
獲取index。

 * 1.將字符串轉換成比較大的數字,這個數字稱為hashCode
 * 2.將大的數字壓縮到數組(size)范圍之內既index

公式:
hashCode = hashCode * prime + key.charCodeAt(i);(快速計算)
var index = hashCode % size;(霍納法則
prime為質數,且小於數組的容量。
size為哈希表的長度

2.哈希表擴容

*1.為什么要擴容?

哈希表中存儲的元素個數不夠存放,或者鏈地址法長度很長。

*2.注意點

一旦擴容,所有數據都要重新插入。
理由:
求余運算,例如12=2,但是如果12%20呢 那就為12了。

*3.什么情況下擴容/縮容

loadFactor = 表中元素個數/表的長度
loadFactor > 0.75 擴容
loadFactor < 0.25 縮容

每次插入或則刪除鍵值對的時候,就進行一次判斷。

 

 


好的哈希函數的特點:
1.快速的計算(通過簡單的運算就可以得到hashCode)
方法:霍納法則

2.均勻的分布
需要使用常量的地方盡量使用質數
例如哈希表的長度為質數(index)


2.解決沖突(既是存放數據如果下標重合的解決方法)

2.1鏈地址法

存放數據的數組里面每一個下標不在是單純的基本數據類型,而是引用類型。
引用類型有兩種:
鏈表結構
數組結構

2.2開放地址法

尋找哈希表里面空白的下標來添加重復的數據
查找重復的數據
找到之后線性往下查到,遇到空值返回
刪除數據
考慮到查找數據的問題,所以刪除數據時將數據賦值為-1。避免重復的數據因為空值查找不到

 

/**
 * 哈希表 鏈地址法
 */
function HashTable() {
    this.storage = [];  // 存放元素
    this.count = 0;  // 存放的元素個數
    this.limit = 7;    // 哈希表的長度 盡量為質數

    /**
     * 哈希函數
     * 作用:返回key在哈希表的中的下標位置
     * 參數:key(String) size(哈希表長度)
     */
    HashTable.prototype.hashFunc = function (key, size) {
        // 1.定義hashCode變量
        var hashCode = 0;

        // 2.霍納法則計算hashCode的值
        for (var i = 0; i < key.length; i++) {
            var zishu = 37;     // 賦值成質數
            hashCode = hashCode * zishu + key.charCodeAt(i);
        }

        // 3.取余操作
        var index = hashCode % size;

        return index;
    }

    /**
     * 作用:添加或者修改操作
     * 參數:key(String) value(Any)
     */
    HashTable.prototype.push = function(key,value) {

        // 1.根據key獲取index
        var index = this.hashFunc(key,this.limit);
        
        // 2.根據index獲取bukcet
        var bucket = this.storage[index];           // bucket 代表存儲相同key的桶
        
        // 3.判斷bucket是否存在,如果不存在 則賦值為[]
        if(bucket == null) {
            bucket = [];
            this.storage[index] = bucket;
        }

        // 4.判斷bucket里面的tuple是修改還是添加
        for(var i = 0; i < bucket.length; i++ ) {
            var temp = bucket[i];
            if(temp[0] == key) {
                temp[1] =  value;
                return;                 // 這里判斷為修改 直接結束該方法
            }
        }
        bucket.push([key,value]);       // 添加

        // 5.當前哈希表元素個數+1
        this.count ++;

        // 6.判斷是否需要擴容
        var loadFactor =  this.count / this.limit;
        if( loadFactor > 0.75) {
            var newLimit = this.limit*2;
            while(!this.isPrime(newLimit)) {
                newLimit++;
            }
            this.reSize(newLimit);
        }

    }
    /**
     * 作用:根據key獲取value
     * 參數: key(String)
     * 返回:找的到返回value 找不到返回null
     */
    HashTable.prototype.get = function(key) {
        //1.根據key獲取index
        var index = this.hashFunc(key,this.limit);

        //2.根據index獲取bucket(array)
        var bucket = this.storage[index];

        //3.判斷bucket是否存在
        if(bucket == null) {
            return null;
        }

        //4.根據key從bucket中獲取tuple(數組)
        var tuple = [];
        for(var i = 0; i < bucket.length; i++) {
            tuple = bucket[i];
            if(tuple[0] == key) {
                break;
            }
        }

        //5.根據tuple返回結果
        return tuple.length == 0 ? null : tuple[1];
    }

    /**
     * 作用:根據key刪除一個truple(array)
     * 參數: key(String)
     * 返回: 刪除的元素的value
     */
    HashTable.prototype.remove = function(key) {
        // 1.根據key找到index
        var index = this.hashFunc(key,this.limit);

        // 2.根據index獲取bucket
        var bucket = this.storage[index];

        // 3.判斷bucket是否存在
        if(bucket == null) return
        
        // 4.根據key從bucket中刪除tuple(數組)
        for(var i = 0; i < bucket.length; i++) {
            var tuple = bucket[i];
            if(tuple[0] == key) {
               // delete tuple;     // 這種可行否? 不行,delete 只對對象的屬性有效
               bucket.splice(i,1);
                break;
            }
        }
        // 5.哈希表長度-1
        this.count--;

        // 6.判斷是否需要縮容
        var loadFactor =  this.count / this.limit;
        if( loadFactor < 0.25  && this.limit < 7) {
            var newLimit = parseInt(Math.floor(limit/2));
            while(!this.isPrime(newLimit)) {
                newLimit++;
            }
            this.reSize(newLimit);
        }

        return tuple[1];
    }

    /**
     * 判斷哈希表是否為空
     */
    HashTable.prototype.isEmpty = function(){
        return this.count == 0 ? true : false;
    }
    /**
     * 判斷哈希表元素的個數
     */
    HashTable.prototype.size = function(){
        return this.count
    }

    /**
     * 哈希表擴容
     * 參數limit哈希表新的長度。
     */
    HashTable.prototype.reSize = function(limit){
        //1.創建oldStorage指向當前storage
        var oldStorage = this.storage;
        //2.重置當前鏈表屬性
        this.storage = [];
        this.limit = limit;
        this.count = 0;
        //3.逐個賦值
        for(var i = 0; i < oldStorage.length; i++){
            var bucket = oldStorage[i];
            if(bucket==null) {
                continue;
            }
            for(var j = 0; j < bucket.length; j++) {
                var tuple = bucket[j];
                this.push(tuple[0],tuple[1]);
            }
        }
    }

    /**
     * 判斷是否為質數
     * 參數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.toString = function(){
        for (const key in this.storage) {
            if (this.storage.hasOwnProperty(key)) {
                let bucket = this.storage[key];
                let str = "";
                for (const key1 in bucket) {
                    if (bucket.hasOwnProperty(key1)) {
                        let  tuple = bucket[key1];
                        str = str + " " + tuple[1];
                    }
                }
                console.log(str);
            }
        }
    }
}

var hash = new HashTable();

hash.push("我","wo")
hash.push("是","shi")
hash.push("你","ni")
hash.push("你3","ni1")
hash.push("你1","ni2")
hash.push("你3","ni3")
hash.push("你5","ni5")
hash.push("你6","ni6")
hash.push("你8","ni8")
hash.push("你9","ni9") 
hash.push("你01","ni10")
hash.push("你1","ni8")
hash.push("你3","ni9")
hash.push("你9","ni10")

hash.toString();
console.log("------------------------------------")
hash.remove("是");
hash.toString();

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM