数据结构与算法(四)之哈希结构(偏向JS)

数据结构(四)

说明:本文基于哔哩哔哩视频【JavaScript数据结构与算法】整理

哈希表(hash)

  1. 它的结构是数组,是通过一种哈希函数将 hashcode,转化成数组的下标

  2. 特性:相对于数组,优点:插入、查询和删除的操作,效率非常高;缺点:无序,不能重复,空间利用率不高;

  3. 相关概念

    • 哈希化:将很大的数字转化成数组范围内下标的过程(取余操作);

    • 哈希函数:将单词转化为大数字hashcode(幂的连乘相加,避免重复),大数字再进行哈希化的代码,封装在一个函数里;

    • 优秀哈希函数的目标:

      • 快速,(霍纳法则:幂的连乘相加,多项式的优化):Pn(x)= anx n+a(n-1)x(n-1)+…+a1x+a0=((…(((anx +an-1)x+an-2)x+ an-3)…)x+a1)x+a0
      • 下标均匀分布:在使用常量的地方, 尽量使用质数,比如 哈希表的长度,N次幂的底数(我们之前使用的是27)
    • 哈希表:最终将数据插入到这个数组,对整个结构的封装,称为一个哈希表

    • 填装因子:总数据项 和 哈希表的总长的比值。填装因子越小,存储效率越低,探测速度越快

    • 注意事项

    1. 取余操作也有余数重复的时候,但是哈希表不能重复,解决这个冲突的方法:
      • 哈希化的效率排名:1️⃣2️⃣3️⃣4️⃣
      • 链地址法(拉链法):哈希化之后的数组单元中存储一个数组或者是链表(根据业务需求:查找用数组,其他用链表);先通过数组下标找到所在位置之后,再在链表或者数组中进行操作。1️⃣
      • 开放地址法:寻找空白的单元格来添加新元素,寻找的方法有:
        • 二次查找(二分查找),在线性查找的基础上对步长做了处理;x,x+12,x+22 2️⃣
        • 线性查找,删除的时候不能将下标设置为null,因为会被特殊处理,可以成-1;x,x+1,x+2 4️⃣
        • 再哈希法;依赖关键字,将关键字做哈希化之后作为 前面哈希函数的步长;哈希化结果和第一次的不一样,不能为0.【 stepSize=constant-(key % constant) , 永远不会为0】 3️⃣
  4. 实现方式:基于数组实现()

  5. 常见操作
    - put 插入或者修改
    - get
    - remove
    - 容量质数化
    - 其他:isEmpty,size

相关操作的代码:

  • 哈希函数
// 哈希函数:将名称 key 转化为下标 值的函数
        // 转化为较大数字:hashcode
        // 将hashcode 转到数组范围(大小)内
        function hashFunc(str, size) {
            var hashcode = 0;
            for (let i = 0; i < str.length; i++) {
            // 底数为质数 31,37等
                hashcode = 37 * hashcode + str.charCodeAt(i);
                console.log(hashcode, str[i], i);

            }
            index = hashcode % 7;
            return index
        }
        // console.log('aa'.charCodeAt(0));
        // hashFunc('cc', 7)
        console.log(hashFunc('22', 11)); //4
        console.log(hashFunc('32', 11)); // 1
        console.log(hashFunc('34', 11)); // 2
  • 判断质数

        // 高效判断  :开平方根 效率较高

        // 一个数,进行因数分解之后的两个数,一个肯定比开平方根之后的数小,一个比开平方根大。既然平方根小的都没被整除,大的也不可能别整出
        //  16= 2 * 8  ,4*4 = 16    1-4之间的数字有一个被整除,则不是质数
        //   17=1*17  ,4.12  * 4.12 ==17   4.12 之前没有数字  能整除,所以是质数
        function isPrimeSqrt(num) {
            if (num == 0 || num == 1) {
                return false
            }
            const temp = parseInt(Math.sqrt(num));
            for (let i = 2; i <= temp; i++) {
                if (num % 2 == 0) {
                    return false;
                }
            }
            return true;
        }

  • 哈希表
        // 哈希表
        // 使用 链地址法 做哈希化   
        // 表结构最终的解构 [[[key,val],[key,val]],[key,val],[key,val],[key,val],[key,val],]
        function HashTable() {
            // 属性
            this.storage = []; //存储空间
            this.count = 0; // 记录当前数据 长度
            this.limit = 3; //表的容量   最好为质数
            // 使容量恒为 质数  :双倍扩容之后 ,肯定不是 质数,可以给二倍数每次加一,进行判断是不是质数

            this.loadFactor = this.count / this.limit; //填充因子  > 0.75要扩容 一倍
            // 方法

            // 哈希函数
            HashTable.prototype.hashFunc = function(str, size) {
                var hashcode = 0;
                // 霍纳法则,转化为很大数,hashcode
                for (var i = 0; i < str.length; i++) {
                    // 37 ,31,41等可供选择
                    hashcode = 37 * hashcode + str.charCodeAt(i);
                    // console.log(hashcode, str[i], i);
                }
                // 取余,返回下标
                index = hashcode % size;
                return index;
            };

            // 插入、修改数据
            // 参数:key:比如是一个名字,value:比如是这个名字对应的身份信息
            HashTable.prototype.put = function(key, value) {
                // 根据key找到 索引所在的位置,
                var index = this.hashFunc(key, this.limit);
                // 根据索引找到该位置放置的元素 buket,这个元素应该是一个数组
                var bucket = this.storage[index];
                //判断bucket 是否为null,为null则创建, 将元素赋值为数组,再将这个数组元素放置到这个位置
                if (bucket === undefined) {
                    bucket = [];
                    this.storage[index] = bucket;
                }
                // console.log(bucket, this.count);

                // 判断bucket 里面是不是存在这个key,
                // 存在则修改
                var override = false;
                for (var i = 0; i < bucket.length; i++) {
                    var tuple = bucket[i];

                    if (tuple[0] == key) {
                        tuple[1] = value;
                        override = true;
                        return;
                    }
                }
                // 不存在则添加
                if (!override) {
                    bucket.push([key, value]);
                    this.count++;
                    //   扩容
                    if (this.count > this.limit * 0.75) {
                        var size = this.limit * 2;
                        var newPrimeSize = this.turnPrime(size);
                        this.resize(newPrimeSize);
                    }
                }
            };

            // 获取
            HashTable.prototype.get = function(key) {
                var index = this.hashFunc(key, this.limit);
                var bucket = this.storage[index];

                if (bucket == null || bucket == undefined) {
                    return null;
                }
                // 线性查找 bucket
                if (bucket) {
                    for (var i = 0; i < bucket.length; i++) {
                        const tuple = bucket[i];
                        if (tuple[0] === key) {
                            return tuple[1];
                        }
                    }
                    return null;
                }
            };
            // 删除
            HashTable.prototype.remove = function(key) {
                var index = this.hashFunc(key, this.limit);
                var bucket = this.storage[index];
                if (bucket == null) {
                    return null;
                }
                for (var i = 0; i < bucket.length; i++) {
                    const tuple = bucket[i];
                    if (tuple[0] == key) {
                        bucket.splice(i, 1);
                        this.count -= 1;

                        //   缩小容量
                        // 避免太小
                        if (this.limit > 7 && this.count < this.limit * 0.25) {
                            var size = Math.floor(this.limit / 2);
                            var newPrimeSize = this.turnPrime(size);
                            this.resize(newPrimeSize);
                        }
                        return tuple[1];
                    }
                }
                return null;
            };

            // isEmpty,size
            HashTable.prototype.isEmpty = function() {
                return this.count == 0;
            };
            HashTable.prototype.size = function() {
                return this.count;
            };

            // 哈希表扩容
            HashTable.prototype.resize = function(newlimit) {
                var oldStorage = this.storage;
                this.storage = [];
                this.count = 0;
                this.limit = newlimit;
                for (let i = 0; i < oldStorage.length; i++) {
                    const bucket = oldStorage[i];

                    if (bucket == null) {
                        continue;
                    }
                    for (let j = 0; j < bucket.length; j++) {
                        const tuple = bucket[j];
                        this.put(tuple[0], tuple[1]);
                    }
                }
            };

            // 判断是不是质数
            HashTable.prototype.isPrime = function(num) {
                if (num == 0 || num == 1) {
                    return false;
                }
                const temp = parseInt(Math.sqrt(num));
                for (let i = 2; i <= temp; i++) {
                    if (num % 2 == 0) {
                        return false;
                    }
                }
                return true;
            }

            // 转化为质数
            HashTable.prototype.turnPrime = function(num) {
                // 给 num 每次加一,直到找到质数、
                while (!this.isPrime(num)) {
                    num++;
                }
                return num;
            };
        }
        const tabs = new HashTable();
        tabs.put("age", 12);
        tabs.put("name", "wei");
        tabs.put("sex", "man");
        tabs.put("sex1", "man");

        console.log(tabs.get("age"), "table");
        console.log(tabs.limit, 'limit');

        tabs.put("name", "sss");
        console.log(tabs.get("name"), "修改");
        //
        tabs.remove("name");
        console.log(tabs.get("name"), "remove");

其他数据结构可访问以下地址:

数据结构与算法(一)之数组,队列,栈(偏向JS)
数据结构与算法(二)之单向链表和双向链表(偏向JS)
数据结构与算法(三)之集合,字典

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值