JavaScript数据结构与算法 -- 哈希表(二)

1.哈希函数

好的哈希函数应该尽可能的让计算的过程变得简单,提高计算的效率

  • 哈希表的主要优点就是他的速度
  • 提高速度的办法就是尽量少用乘法和除法,因为其性能较低。

那么如何设计?

  • 哈希表的优势在于效率,所以快速获取到对应的hashCode非常重要
  • 我们需要快速的计算来获取到元素的对应的hashCode
  • 让元素再哈希表中均匀分布

2.使用秦九韶算法实现快速计算。

  • 例如cats = 327^3 + 127^2 + 20*27 + 17
  • 使用秦九韶算法 ( (3*27+1)*27^2 + 20)*27 + 17
  • 计算再变换后我们需要多少次乘法,多少次加法
  • 时间复杂度从O(N^2) -> O(N)

3.为了实现均匀分布,最好将常量使用为质数

  • 哈希表长度使用质数
  • 幂的底数使用质数

4.哈希函数的实现

 //设计哈希函数
        // 1> 将字符串转换为比较大的数字 hashCode
        // 2> 将大的数字压缩到某个数组范围(大小)内
        // 字符串 str   数组范围,数组大小size
        function hashFunc(str, size){
            //1.定义hashCode变量
            var hashCode = 0;

            //2.秦九韶算法,来计算hashCode的值
            //abc为例
            for(var i = 0; i < str.length; i++){
                hashCode = 37 * hashCode + str.charCodeAt(i);
                //相当于秦九韶算法每次都乘以37
                //37 * 0 + 97
                //37*97 + 98
                //37 *(37*97 + 98)+ 99
            }

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

            return index;
        }
        // 测试哈希函数
        alert(hashFunc('abc', 7));  //4
        alert(hashFunc('cba', 7));  //3
        alert(hashFunc('nba', 7));  //5
        alert(hashFunc('mba', 7));  //1
        // alert(hashFunc('abc', 7));
        

5.创建哈希表

  • 我们这里采用链地址法实现哈希表,每个index对应一个数组,防染链表也行
  • 数组里面我们继续放一个数组[key,value]的形式
  • 最后我们创建的哈希表数据形式是:[[k,v],[k,v],[k,v],[k,v]]
  • 定义三个属性:
    • storage 作为我们的数组,存放相关的元素
    • count 表示当前已经存放了多少数据
    • limit 用于标记数组中一共可以放多少个元素
/封装哈希表的类
        function hashTable(){
            // 属性
            this.storage = [];
            
            this.count = 0;
            //用来计算装载因子,大于0.75我们要进行扩容,小于0.25要将数组变小
            
            this.limit = 7;
            //哈希表里面的数组的总长度
            // 方法
        }

6.哈希表相关的操作方法

插入和修改元素
  • 哈希表的插入和修改方法是一个方法
  • 当传入的<key,value>时不存在这个key时就是插入操作,存在这个key就是修改
  • 第一件事情:根据我们传入的key获取索引值,将我们的数据插入到对应的位置
  • 第二件事情:根据索引值取出bucket
    • 如果不存在bucket,那么就要创建bucket,并且放在该索引的位置
  • 判断我们是新增还是修改原来的值
    • 如果已经有值了那么就修改
    • 如果没有就执行后续的添加操作
//插入和修改方法
    HashTable.prototype.put = function (key, value) {
        //1.首先根据哈希函数获取到index
        var index = this.hashFunc(key, this.limit);

        //2.根据index找到bucket
        var bucket = this.storage[index];

        //3.判断buckjet是否存在,不存在的话新建一个bucket并且将当前的bucket添加进数组
        if (bucket == null) {
            bucket = [];
            this.storage.push(bucket);
        }

        //4.如果有值了就修改
        for (var i = 0; i < bucket.length; i++) {
            var tuple = bucket[i];
            if (tuple[0] == key) {
                tuple[1] = value;
                return "修改成功";
                //修改的话就不用再继续执行下去了。
            }

        }

        //5.执行到这一步说明之前不是修改数据而是新增数据。
        bucket.push([key, value]);
        this.count += 1;
    }
获取操作
  • 第一步是根据key获取对应的index
  • 第二部根据index获取对应的bucket
  • 第三步判断bucket是否为null,如果为null直接返回null
  • 第四步线性查找bucket中锋的每一个key是否等于传入的key,如果等于直接返回对应的value
 //获取操作
   //插入和修改方法
    HashTable.prototype.put = function (key, value) {
        //1.首先根据哈希函数获取到index
        var index = this.hashFunc(key, this.limit);

        //2.根据index找到bucket
        var bucket = this.storage[index];

        //3.判断buckjet是否存在,不存在的话新建一个bucket并且将当前的bucket添加进数组
        if (bucket == null) {
            bucket = [];
            this.storage.push(bucket);
        }

        //4.如果有值了就修改
        for (var i = 0; i < bucket.length; i++) {
            var tuple = bucket[i];
            if (tuple[0] == key) {
                tuple[1] = value;
                return "修改成功";
                //修改的话就不用再继续执行下去了。
            }

        }

        //5.执行到这一步说明之前不是修改数据而是新增数据。
        bucket.push([key, value]);
        this.count += 1;
    }
删除操作
  • 根据我们的key获取对应的index
  • 根据index来获取我们的bucket
  • 判断这个bucket是否存在,如果不存在,那么直接返回false,或者把删除的东西返回,null
  • 线性查找bucket,寻找相应的数据,并且删除
  • 依然没有找到,那么返回null
 //删除操作
    //删除操作
HashTable.prototype.remove = function(key){
    //1.根据key来寻找index
    var index = this.hashFunc(key,this.limit);

    //2.根据index寻找对应的bucket
    var bucket = this.storage[index];

    //3.判断桶是否存在
    if(bucket == null){
        return "删除失败";
    }

    //4.线性查找bucket,并且删除
    for(var i = 0 ; i < bucket.length; i ++){
        var tuple = bucket[i];
        if(tuple[0] == key){
            bucket.splice(i,1);
            this.count -= 1;
            return tuple[1];
        }
    }


    //5.遍历完依旧没有找到
    return "删除失败"
}
完整操作以及代码测试
//封装哈希表类
function HashTable() {
    // 属性
    this.storage = [];

    this.count = 0;
    //用来计算装载因子,大于0.75我们要进行扩容,小于0.25要将数组变小

    this.limit = 7;
    //哈希表的长度

    //1.设计哈希函数
    HashTable.prototype.hashFunc = function (str, size) {
        var hashCode = 0;
        for (var i = 0; i < str.length; i++) {
            hashCode = 37 * hashCode + str.charCodeAt(i);
        }
        index = hashCode % size;
        return index;
    }

    //方法
    //插入和修改方法
    HashTable.prototype.put = function (key, value) {
        //1.首先根据哈希函数获取到index
        var index = this.hashFunc(key, this.limit);

        //2.根据index找到bucket
        var bucket = this.storage[index];

        //3.判断buckjet是否存在,不存在的话新建一个bucket并且将当前的bucket添加进数组
        if (bucket == null) {
            bucket = [];
            this.storage.push(bucket);
        }

        //4.如果有值了就修改
        for (var i = 0; i < bucket.length; i++) {
            var tuple = bucket[i];
            if (tuple[0] == key) {
                tuple[1] = value;
                return "修改成功";
                //修改的话就不用再继续执行下去了。
            }

        }

        //5.执行到这一步说明之前不是修改数据而是新增数据。
        bucket.push([key, value]);
        this.count += 1;
    }

    //获取操作
    HashTable.prototype.get = function (key) {
        //1.根据key来找到index
        var index = hashFunc(key, this.limit);

        //2.根据index寻找对应的bucket
        var bucket = this.storage[index];

        //3.判断bucket是否为空
        if (bucket == null) {
            return null;
        }

        //4.如果有bucket的话,就线性查找
        for (var i = 0; i < bucket.length; i++) {
            var tuple = bucket[i];
            if (tuple[0] == key) {
                return tuple[1];
            }
        }

        //5.如果线性查找不到就为null
        return null;
    }

    //删除操作
    HashTable.prototype.remove = function (key) {
        //1.根据key来寻找index
        var index = this.hashFunc(key, this.limit);

        //2.根据index寻找对应的bucket
        var bucket = this.storage[index];

        //3.判断桶是否存在
        if (bucket == null) {
            return "删除失败";
        }

        //4.线性查找bucket,并且删除
        for (var i = 0; i < bucket.length; i++) {
            var tuple = bucket[i];
            if (tuple[0] == key) {
                bucket.splice(i, 1);
                this.count -= 1;
                return tuple[1];
            }
        }


        //5.遍历完依旧没有找到
        return "删除失败"
    }

    //其他方法
    //判断哈希表是否为null
    HashTable.prototype.isEmpty = function () {
        return this.count == 0;
    }

    //返回哈希表的长度
    HashTable.prototype.size = function () {
        return this.count;
    }
}





var ht = new HashTable();
console.log("新增数据————————————————————————")
ht.put('1', 'aaa');
ht.put('2', 'bbb');
ht.put('3', 'ccc');
ht.put('4', 'ddd');
console.log(ht);
console.log("修改数据————————————————————————")
ht.put('1', 'nba');
ht.put('2', 'cba');
console.log(ht);
console.log("删除数据————————————————————————")
ht.remove('1');
ht.remove('5');
console.log(ht);
</script>

测试结果:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值