数据结构-哈希表

2021-07-23  本文已影响0人  AAA前端

哈希表

哈希表是一种数据结构,它可以提供快速的插入和查找操作。它的优点多得难以置信,无论哈希表中有多少数据,插入和删除只需要接近常量的时间,即O(1)的时间级

哈希化

通过映射方法(即哈希函数)把一个巨大的整数范围转化为一个可接受的数组范围中。对哈希表来说是把较大的关键字值范围压缩成较小的数组下标范围

哈希化过程(哈希函数步骤):

为什么要哈希化

哈希函数的要求

举例说明

冲突

把巨大的数字空间压缩成较小的数字空间,插入时不能保证每个关键字都能通过哈希函数映射到数组的空白单元,删除时不能保证每个关键字通过哈希函数映射到的单元数据项正好为要删除的数据项。

  1. 减少冲突——哈希表数组容量取质数
  1. 开放地址法
  1. 链地址法

哈希算法

    function hashFunc(str, max) {
        // 1.初始化hashCode的值
        var hashCode = 0

        // 算法 实际用现成的 unidcode转码就行。这里为了展示原理,自己实现
        // 比如 215 ===》 我们用 2*37^2+1*37^1+5 --->抽象成表达式  a(n)X^n+a(n-1)X^n-1...a(1)X+a(0)
        // 乘法次数是 n+(n-1)+(n-2)....+1 = n(n-1)/2 次
        // 加法次数是 n次
        // 时间复杂度 是 O(N^2)
        // 霍纳算法---- ((...(anX + an-1)X + an-2)X+ an-3)...)X+a1)X+a0
        // 乘法次数是 n次
        // 加法次数是 n次
        // 时间复杂度 是 O(N)
        // 来计算hashCode的数值
        for (var i = 0; i < str.length; i++) {
            // 37 随便取的一个质数  charCodeAt获取字符串的编码
            hashCode = 37 * hashCode + str.charCodeAt(i)
        }

        // 3.取模运算
        hashCode = hashCode % max
        return hashCode
    }

哈希表

        // 创建HashTable构造函数  开放地址法实现
        function HashTable() {
            // 定义属性
            this.storage = []
            this.count = 0 // 存储的数组长度
            this.limit = 8 // 装填因子  数据项个数/哈希表数组容量

            // 定义相关方法
            // 判断是否是质数 (质数的两个数,一个小于等于平方根,一个大于等于平方根,所以有下面的算法)
            HashTable.prototype.isPrime = function(num) {
                // 平方根 后 取整
                var temp = parseInt(Math.sqrt(num))
                    // 2.循环判断
                for (var i = 2; i <= temp; i++) {
                    if (num % i == 0) {
                        return false
                    }
                }
                return true
            }

            // 获取质数
            HashTable.prototype.getPrime = function(num, fn) {
                while (!isPrime(num)) {
                    num++
                }
                fn(num)
                return num
            }

            // 哈希函数
            HashTable.prototype.hashFunc = function(str, max) {
                // 1.初始化hashCode的值
                var hashCode = 0

                // 2.霍纳算法, 来计算hashCode的数值
                for (var i = 0; i < str.length; i++) {
                    hashCode = 37 * hashCode + str.charCodeAt(i)
                }

                // 3.取模运算
                hashCode = hashCode % max
                return hashCode
            }

            // 插入数据方法 即是插入也是修改
            HashTable.prototype.put = function(key, value) {
                // 1.获取key对应的index
                var index = this.hashFunc(key, this.limit)

                // 2.取出数组(也可以使用链表)
                // 数组中放置数据的方式: [[ [k,v], [k,v], [k,v] ] , [ [k,v], [k,v] ]  [ [k,v] ] ]
                var bucket = this.storage[index]

                // 3.判断这个数组是否存在
                if (bucket === undefined) {
                    // 3.1创建桶
                    bucket = []
                    this.storage[index] = bucket
                }

                // 4.判断是新增还是修改原来的值.
                var override = false
                for (var i = 0; i < bucket.length; i++) {
                    var tuple = bucket[i]
                    if (tuple[0] === key) {
                        tuple[1] = value
                        override = true
                    }
                }

                // 5.如果是新增, 前一步没有覆盖
                if (!override) {
                    bucket.push([key, value])
                    this.count++

                        // 判断 当前存储的 数组长度 是否 大于 2/3 大于需要扩容
                        if (this.count > this.limit * 0.75) {
                            this.getPrime(this.limit * 2, this.resize)
                        }
                }
            }

            // 获取存放的数据
            HashTable.prototype.get = function(key) {
                // 1.获取key对应的index
                var index = this.hashFunc(key, this.limit)

                // 2.获取对应的bucket
                var bucket = this.storage[index]

                // 3.如果bucket为null, 那么说明这个位置没有数据
                if (bucket == null) {
                    return null
                }

                // 4.有bucket, 判断是否有对应的key
                for (var i = 0; i < bucket.length; i++) {
                    var tuple = bucket[i]
                    if (tuple[0] === key) {
                        return tuple[1]
                    }
                }

                // 5.没有找到, return null
                return null
            }

            // 删除数据
            HashTable.prototype.remove = function(key) {
                // 1.获取key对应的index
                var index = this.hashFunc(key, this.limit)

                // 2.获取对应的bucket
                var bucket = this.storage[index]

                // 3.判断同是否为null, 为null则说明没有对应的数据
                if (bucket == null) {
                    return null
                }

                // 4.遍历bucket, 寻找对应的数据
                for (var i = 0; i < bucket.length; i++) {
                    var tuple = bucket[i]
                        // 找到对应
                    if (tuple[0] === key) {
                        bucket.splice(i, 1)
                        this.count--

                            // 缩小数组的容量
                            if (this.limit > 7 && this.count < this.limit * 0.25) {
                                this.getPrime(Math.floor(this.limit / 2), this.resize)
                            }
                    }
                    return tuple[1]
                }

                // 5.来到该位置, 说明没有对应的数据, 那么返回null
                return null
            }

            // isEmpty方法
            HashTable.prototype.isEmpty = function() {
                return this.count == 0
            }

            // size方法
            HashTable.prototype.size = function() {
                return this.count
            }

            // 哈希表扩容
            HashTable.prototype.resize = function(newLimit) {
                // 1.保存旧的数组内容
                var oldStorage = this.storage

                // 2.重置属性
                this.limit = newLimit
                this.count = 0
                this.storage = []

                // 3.遍历旧数组中的所有数据项, 并且重新插入到哈希表中
                oldStorage.forEach(function(bucket) {
                    // 1.bucket为null, 说明这里面没有数据
                    if (bucket == null) {
                        return
                    }

                    // 2.bucket中有数据, 那么将里面的数据重新哈希化插入
                    for (var i = 0; i < bucket.length; i++) {
                        var tuple = bucket[i]
                        this.put(tuple[0], tuple[1])
                    }
                }).bind(this)
            }
        }


参考:https://blog.csdn.net/ChenTianyu666/article/details/106396685

上一篇 下一篇

猜你喜欢

热点阅读