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