哈希表

339 阅读2分钟

哈希表定义

  • 是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表, 也就是哈希表.

自定义哈希表的代码实现

// 根据链地址法实现哈希表
function HashTable(){
    // 属性
    this.storage = [];
    this.count = 0;
    this.limit = 7;
    
     // 哈希函数
    HashTable.prototype.hashFunc = function(str, size){
        // 1.定义hashcode变量 
        let hashCode = 0;
        
        // 2.使用霍纳算法计算hashcode
        for (let i = 0; i < str.length; i++) {
          hashCode = 37 * hashCode + str.charCodeAt(i);
        }
        
        // 3. 取余操作
        let index = hashCode % size;
        
        return index;
    }
    
    // 插入&修改的操作
    HashTable.prototype.put = function(key, value){
    /*
      1.根据key获取索引值,将数据插入到对应的位置。
      2.根据索引值取出bucket
        1)如果桶不存在,就创建一个bucket放在索引所在的位置
      3.判断新增还是修改原来的值
        1)如果在桶中已经存在该元素,就替换它的值
        2)如果不存在,就直接添加到桶中。
      4.新增操作
    */ 
        // 1.获取对应key的index
        let index = this.hashFunc(key,  this.limit);
        
        // 2.根据对应的index获取桶
        let bucket = this.storage[index];
        if(bucket == null){
          bucket = [];
          this.storage[index] = bucket;
        }
        
        // 3.判断是替换还是新增
        for (let i = 0; i < bucket.length; i++) {
          const tuple = bucket[i];
          if (tuple[0] == key){
            tuple[1] = value;
            return
          }
        }
        
        // 4.如果没有找到,就是新增
        bucket.push([key, value]);
        this.count += 1;
        
        // 5. 判断是否要进行扩容 装载因子如果大于0.75,就需要对数组进行扩容
        if (this.count > this.limit * 0.75) {
          let tempLimit = this.limit * 2;
          let newLimit = this.getPrime(tempLimit); //转换为质数
          this.resize(newLimit);
        }
    }
    
    // 查询
    HashTable.prototype.get = function(key){
        // 1. 通过key获取对应的index
        let index = this.hashFunc(key, this.limit);

        // 2. 根据对应的index获取该位置的桶 bucket
        let bucket = this.storage[index];

        // 3.如果桶不存在,那么该元素肯定也不存在,直接返回null
        if(bucket == null){
          return null;
        }

        // 4.如果桶存在,就遍历找到和key对应的数组
        for (let i = 0; i < bucket.length; i++) {
          const tuple = bucket[i];
          if (tuple[0] == key) {
            return tuple[1];
          }
        }

        // 5.说明桶中没有找到,说明不存在,直接返回null
        return null;
    }
    
    // 删除元素
    HashTable.prototype.delete = function(key){
        // 1.获取key对应的index
        let index = this.hashFunc(key, this.limit);

        // 2.获取该index的bucket
        let bucket = this.storage[index];

        // 3.判断bucket是否存在
        if(bucket == null) return null;

        // 4.找到当前key所在的桶中的位置
        for (let i = 0; i < bucket.length; i++) {
          const tuple = bucket[i];
          if (tuple[0] == key) {
            bucket.splice(i, 1);
            this.count--;

            // 5. 判断是否需要缩小容量
            if (this.limit > 7 && this.count / this.limit * 0.25) {
              let tempLimit = Math.floor(this.limit / 2);
              let newLimit = this.getPrime(tempLimit);
              this.resize(newLimit);
            }

            return tuple[1];
          }
        }

        // 5.说明没有找到
        return null;
    }
    
    // Hash表是否为空
    HashTable.prototype.isEmpty = function(){
        return this.count == 0;
    }

    // Hash表的长度
    HashTable.prototype.size = function(){
        return this.count;
    }
    
    // hash表的扩容或者缩容
    HashTable.prototype.resize = function(newLimit){
        // 1.保存原有的hash表
        let oldStorage = this.storage;

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

        for (let i = 0; i < oldStorage.length; i++) {
          const bucket = storage[i];
          if (bucket) {
            for (let j = 0; j < bucket.length; j++) {
              const tuple = bucket[j];
              this.put(tuple[0], tuple[1]);
            }
          }
        }
    }

    // 判断某个值是否是质数
    HashTable.prototype.isPrime = function(num){
        // 1.获取num的平方根, 如果某一个数字能分解因子,那么它能拆成的因子一个比她的平方根小,
        // 另一个比平方根大,所以我们只要比对到平方根的位置,就能判断是否能整除

        let temp = parseInt(Math.sqrt(num));
        for (let index = 2; index < temp; index++) {
            if (num % index === 0) {
                return false
            }
        }

        return true;
    }

    // 获取质数的方法
    HashTable.prototype.getPrime = function(num){
        while (!this.isPrime(num)) {
          // 如果num不是质数,然后就+1,然后再判断,一直找到是质数为止,然后跳出循环,直接返回
          num++;
        }

        return num
    }
}

哈希表的应用

  • 题目:两数之和

  • 给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target  的那 两个 整数,并返回它们的数组下标。

  • 代码实现

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[]}
 */
var twoSum = function(nums, target) {
    let map = new Map();
    for(let i = 0; i <= nums.length - 1; i++){
        let value = nums[i];
        let different = target - value;
        if(map.has(different)){
            return [map.get(different), i];
        }else{
            map.set(value, i);
        }
    }
};