数据结构:哈希表

79 阅读2分钟

哈希表

基于数组实现,
优点
比数组更快的插入和删除查找,比数还快。
哈希表中数据是没有顺序的。
通常情况下,key是不允许重复的

难点1:字母转数字方案,保证不重复
方案一:数字相加(数字太小,很容易重复,不合理)
自己设计一个编码譬如a:1,b:2,c:3...
cats转换成数字就是3+1+20+19=43;43作为单词下标存储在数组中
方案二:幂的连乘(基本保持一致,空间很浪费)
cats转换成数字327^3+127^2+2027^1+1927^0

哈希化:将大数字转化成数组范围内下标的过程。
哈希函数:将单词转成大数字,大数字在进行哈希化的代码实现放在一个函数中
哈希表:吧数据插入到这个数组,对整个结构的封装。

解决重复问题

主要有两种方法 1、链地址法(拉链法)装载因子可能大于1 (线性) java hashmap 2、开放地址法 装载因子1 1、链地址法解决冲突 图片: uploader.shimo.im/f/R8OTbmCDI…

2、开放地址法 产生冲突,寻找空白的单元格来存放数据。会出现数据的聚集,降低哈希的效率。
哈希表不仅仅要存删找还要扩容,扩容保证哈希的效率,getPrime保证扩容的数字是质数,质数可以让数据分布的更加均匀

if (this.count > this.limit * 0.75) {

    var primeNum = this.getPrime(this.limit * 2)

    this.resize(primeNum)

}

  // 创建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) {
            while (!isPrime(num)) {
                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++


                if (this.count > this.limit * 0.75) {
                    var primeNum = this.getPrime(this.limit * 2)
                    this.resize(primeNum)
                }
            }
        }


        // 获取存放的数据
        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) {
                        var primeNum = this.getPrime(Math.floor(this.limit / 2))
                        this.resize(primeNum)
                    }
                }
                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)
        }
    }


    // 测试哈希表
    // 1.创建哈希表
    var ht = new HashTable()


    // 2.插入数据
    ht.put("abc", "123")
    ht.put("cba", "321")
    ht.put("nba", "521")
    ht.put("mba", "520")


    // 3.获取数据
    alert(ht.get("abc"))
    ht.put("abc", "111")
    alert(ht.get("abc"))


    // 4.删除数据
    alert(ht.remove("abc"))
    alert(ht.get("abc"))