1.概念

  哈希表是一种根据关键码去寻找值的数据映射结构,该结构通过把关键码映射的位置去寻找存放值的地方。给定表M,存在函数f(key),对任意给定的关键字值(key),代入函数后若能得到包含该关键字的记录在表中的地址,则称表M为哈希表,函数f(key)为哈希函数。

哈希冲突:对不同的关键字可能得到同一散列地址,即k1≠k2,而f(k1)=f(k2)。为了解决冲突,常见的有两种做法:链式地址法、开放地址法。

(1)链式地址法:一旦出现相同的关键码,则将该元素加入到链式尾部。如下图所示。

(2)开放地址法:寻找空白的单元格来添加重复的数据。如下图所示。

  其中11和31及81本应该放在下标为1的位置,由于重复,就近放置

 

 2.封装哈希表

// 创建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)
  }
}
posted on 2021-03-14 20:49  李起桉  阅读(131)  评论(0)    收藏  举报