哈希表理论

图文并茂详解数据结构之哈希表open in new window

数据结构(九)之哈希表理论open in new window

小结

  1. 哈希表是数组实现的,查找和插入都非常快
  2. 哈希化是说一种让字符串转数字的方法,键/关键字这种英文字符串能够转为数字作为数组下标。哈希化可能会造成冲突。因此,哈希表虽然是个数组,如果解决冲突的时候使用拉链法,它的元素是一个引用,也即数组或者链表(bucket,桶)。此外解决冲突的方法还有开放地址法
  3. 哈希函数 是哈希化的手段,涉及霍纳法则。
// 哈希函数
function hashFunc(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
}
复制成功
1
2
3
4
5
6
7
8
9
10
11
12
13
14

哈希表实现

以数组bucket 为例,哈希表存储[key,value]的时候,根据key的哈希函数结果得到数组下标值找到bucket,首先根据key看看bucket有没有存储过,是修改就标记一下flag = true。是新增的话bucket.push(Tuple [key,value]),然后通过!flag改一下哈希表的count属性。

哈希表的组成(含hashFunc)

// 创建HashTable构造函数
function HashTable() {
    // 定义属性
    this.storage = []
    this.count = 0
    this.limit = 8

    // 定义相关方法
    // 哈希函数
    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
    }
}
复制成功
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

哈希表的插入和修改

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

    // 2.取出数组(也可以使用链表)
    var bucket = this.storage[index]

    // 3.判断这个数组是否存在
    if (bucket === undefined) {
        // 3.1创建桶
        bucket = []
        this.storage[index] = bucket
    }
    alert(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++
    }
}
复制成功
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

其他方法

https://www.jianshu.com/p/70c11dc8ec98

哈希表的扩容(含哈希表的删除)

// 增删的时候需要哈希表扩容
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

缩容实际执行:

// 哈希表删除数据
// 根据对应的key, 删除对应的key/value
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
}
复制成功
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

resize执行前需预操作。

1.判断一个数是质数:

①根据定义去写

②temp取开方取整,取模为0则不为质数(一个数的两个公因数必然一个小于temp,一个大于temp,根据temp循环足够。)

function isPrime(num) {
    // 1.获取平方根
    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
}
复制成功
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
晓露寝安浅云逍遥十漾轻拟