数据结构与算法-哈希表

106 阅读3分钟

1、实现哈希表和基本方法

image.png

1、哈希表的一些概念:

  • 哈希化:大数字转化成数组范围内下标的过程,称之为哈希化
  • 哈希函数:我们通常会将单词转化成大数字,把大数字进行哈希化的代码实现放在一个函数中,该函数就称为哈希函数
  • 哈希表:对最终数据插入的数组进行整个结构的封装,得到的就是哈希表

2、哈希表的常见操作为:

  • put(key,value):插入或修改操作;
  • get(key):获取哈希表中特定位置的元素;
  • remove(key):删除哈希表中特定位置的元素;
  • isEmpty():如果哈希表中不包含任何元素,返回trun,如果哈希表长度大于0则返回false;
  • size():返回哈希表包含的元素个数;
  • resize(value):对哈希表进行扩容操作;

3、哈希表的实现

/**
 * 将存入的key通过哈希函数解析为数组的下标
 * 将数组的每个值作为一个链表
 * 将要存入的key、value保存在链表中
*/
//哈希表
function HashTable() {
  this.storage = [] 
  this.count = 0 //计算已经存储的元素个数
  this.limit = 7 //装填因子:loadFactor > 0.75时需要扩容;loadFactor < 0.25时需要减少容量

  /**
   * 哈希函数
   * 通过霍纳法则计算hashCode的值,通过取余实现哈希化
  */
  HashTable.prototype.hashFun = function(key, size) {
    let hashCode = 0

    for (let i = 0; i < key.length; i++) {
      //将字符串转化为较大的数字hashCode
      hashCode = 37 * hashCode + key.charCodeAt(i)                   
    }
    //将hashCode压缩到数组长度之内
    let index = hashCode % size
    return index
  }

  HashTable.prototype.put = function(key, val) {
    //根据key获取对应的inedx
    let index = this.hashFun(key, this.limit)
    //根据index取到对应的链表bucket
    let bucket = this.storage[index]

    if(bucket == null) {
      bucket = []
      this.storage[index] = bucket
    }

    for (let i = 0; i < bucket.length; i++) {
      if(bucket[i][0] === key) {
        bucket[i][1] = val
      }
      return
    }

    bucket.push([key, val])
    this.count += 1

    //判断是否需要扩容操作
    if(this.count > this.limit * 0.75){
      let newSize = this.limit * 2
      let newPrime = this.getPrime(newSize)
      this.resize(newPrime)
    }
  }

  HashTable.prototype.get = function(key) {
    let index = this.hashFun(key, this.limit)
    let bucket = this.storage[index]

    if(bucket == null) {
      return
    }

    for (let i = 0; i < bucket.length; i++) {
      if(bucket[i][0] === key) {
        return bucket[i][1]
      }
    }

    return null
  }

  HashTable.prototype.remove = function(key) {
    let index = this.hashFun(key, this.limit)
    let bucket = this.storage[index]

    if(bucket == null) {
      return null
    }

    for (let i = 0; i < bucket.length; i++) {
      let tmp = bucket[i]
      if(tmp[0] === key) {
        bucket.splice(i, 1)
        this.count -= 1
        //缩小容量
        if (this.limit > 7 && this.count < this.limit * 0.25) {
          let newSize = Math.floor(this.limit / 2)
          let newPrime = this.getPrime(newSize)
          this.resize(newPrime)
        }
        return tmp[1]
      }
    }

    return null
  }

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

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

  //哈希表的扩容
  HashTable.prototype.resize = function(newLimit){
    //1.保存旧的storage数组内容
    let oldStorage = this.storage
  
    //2.重置所有的属性
    this.storage = []
    this.count = 0
    this.limit = newLimit
  
    //3.遍历oldStorage中所有的bucket
    for (let i = 0; i < oldStorage.length; i++) {
      //3.1.取出对应的bucket
      const bucket = oldStorage[i];
  
      //3.2.判断bucket是否为null
      if (bucket == null) {
        continue
      }      
  
      //3.3.bucket中有数据,就取出数据重新插入
      for (let j = 0; j < bucket.length; j++) {
        const tuple = bucket[j];
        this.put(tuple[0], tuple[1])//插入数据的key和value
      }
    }
  }
  
  //判断传入的num是否质数
  HashTable.prototype.isPrime = function(num){
    if (num <= 1) {
      return false
    }
    //1.获取num的平方根:Math.sqrt(num)
    //2.循环判断
    for(var i = 2; i<= Math.sqrt(num); i++ ){
      if(num % i == 0){
        return false;
      }
    }
      return true;
  }
  
  //获取质数的方法
  HashTable.prototype.getPrime = function(num){
     //7*2=14,+1=15,+1=16,+1=17(质数)
    while (!this.isPrime(num)) {
      num++
    }
    return num
  }
}

let hash = new HashTable()

hash.put('person1', 'Tom')
hash.put('person2', 'Bob')
hash.put('person3', 'York')

console.log('put:', hash);

console.log('get', hash.get('person2'));
console.log('get', hash.get('person4'));

console.log('remove', hash.remove('person1'), hash);
console.log('remove', hash.remove('person5'), hash);

console.log('isEmpty', hash.isEmpty());

console.log('size', hash.size());

hash.put('person4', 'Lisa')
hash.put('person5', 'Joy')
hash.put('person6', 'Tomsen')
hash.put('person7', 'Key')
hash.put('person8', 'Ken')
console.log('hash size:', hash.size());
console.log('hash length', hash, hash.limit);
console.log('remove', hash.remove('person2'), hash);
console.log('remove', hash.remove('person3'), hash);
console.log('remove', hash.remove('person4'), hash);
console.log('remove', hash.remove('person5'), hash);
console.log('hash size:', hash.size());
console.log('hash length', hash, hash.limit);