封装哈希表的几个常见方法

index.js的方法封装

/**
 * 设计哈希函数,将传入的字符串哈希化,转换成 hashCode
 * @param string 要哈希化的字符串
 * @param limit 哈希表的最大个数(数组长度)
 * @returns {number} hashCode
 */
export function hashFn(string, limit = 7) {

  // 自己采用的一个质数(无强制要求,质数即可)
  const PRIME = 31;

  // 1、定义存储 hashCode 的变量
  let hashCode = 0;

  // 2、使用霍纳法则(秦九韶算法),计算 hashCode 的值
  for (let item of string) {
    hashCode = PRIME * hashCode + item.charCodeAt();
  }

  // 3、对 hashCode 取余,并返回
  return hashCode % limit;
}


/**
 * 判断一个数是否为质数
 * @param number
 * @returns {boolean}
 */
// 方法一,性能比较低
// export function isPrime(number) {
//   if (number <= 1) return false;
//   for (let i = 2; i < number; i++) {
//     if (number % i === 0) {
//       return false;
//     }
//   }
//   return true;
// }

// 方法二,性能较好
export function isPrime(number) {
  if (number <= 1 || number === 4) return false;
  const temp = Math.ceil(Math.sqrt(number));
  for (let i = 2; i < temp; i++) {
    if (number % i === 0) {
      return false;
    }
  }
  return true;
}

// 哈希表的封装
export class HashTable {

  constructor() {
    this.storage = []; // 哈希表存储数据的变量
    this.count = 0; // 当前存放的元素个数
    this.limit = 7;  // 哈希表长度(初始设为质数 7)

    // 装填因子(已有个数/总个数)
    this.loadFactor = 0.75;
    this.minLoadFactor = 0.25;
  }

  // getPrime(number) 根据传入的 number 获取最临近的质数
  getPrime(number) {
    while (!isPrime(number)) {
      number++;
    }
    return number;
  }

  // put(key, value) 往哈希表里添加数据
  put(key, value) {

    // 1、根据 key 获取要映射到 storage 里面的 index(通过哈希函数获取)
    const index = hashFn(key, this.limit);

    // 2、根据 index 取出对应的 bucket
    let bucket = this.storage[index];

    // 3、判断是否存在 bucket
    if (bucket === undefined) {
      bucket = [];  // 不存在则创建
      this.storage[index] = bucket;
    }

    // 4、判断是插入数据操作还是修改数据操作
    for (let i = 0; i < bucket.length; i++) {
      let tuple = bucket[i]; // tuple 的格式:[key, value]
      if (tuple[0] === key) { // 如果 key 相等,则修改数据
        tuple[1] = value;
        return; // 修改完 tuple 里数据,return 终止,不再往下执行。
      }
    }

    // 5、bucket 新增数据
    bucket.push([key, value]); // bucket 存储元组 tuple,格式为 [key, value]
    this.count++;

    // 判断哈希表是否要扩容,若装填因子 > 0.75,则扩容
    if (this.count / this.limit > this.loadFactor) {
      this.resize(this.getPrime(this.limit * 2));
    }

  }

  // 根据 get(key) 获取 value
  get(key) {

    const index = hashFn(key, this.limit);
    const bucket = this.storage[index];

    if (bucket === undefined) {
      return null;
    }

    for (const tuple of bucket) {
      if (tuple[0] === key) {
        return tuple[1];
      }
    }
    return null;
  }

  // remove(key) 删除指定 key 的数据
  remove(key) {

    const index = hashFn(key, this.limit);
    const bucket = this.storage[index];

    if (bucket === undefined) {
      return null;
    }

    // 遍历 bucket,找到对应位置的 tuple,将其删除
    for (let i = 0, len = bucket.length; i < len; i++) {
      const tuple = bucket[i];
      if (tuple[0] === key) {
        bucket.splice(i, 1); // 删除对应位置的数组项
        this.count--;

        // 根据装填因子的大小,判断是否要进行哈希表压缩
        if (this.limit > 7 && this.count / this.limit < this.minLoadFactor) {
          this.resize(this.getPrime(Math.floor(this.limit / 2)));
        }

        return tuple;
      }

    }

  }

  isEmpty() {
    return this.count === 0;
  }

  size() {
    return this.count;
  }

  // 重新调整哈希表大小,扩容或压缩
  resize(newLimit) {

    // 1、保存旧的 storage 数组内容
    const oldStorage = this.storage;

    // 2、重置所有属性
    this.storage = [];
    this.count = 0;
    this.limit = newLimit;

    // 3、遍历 oldStorage,取出所有数据,重新 put 到 this.storage
    for (const bucket of oldStorage) {
      if (bucket) {
        for (const b of bucket) {
          this.put(b[0], b[1]);
        }
      }

    }
  }
}

调用测试

​
import { hashFn, HashTable, isPrime } from './hashTable';
// ---------------- 封装的哈希表结构测试 ---------------- //
console.log('// ----- 哈希表结构测试 START -----//');

console.log('=== START 哈希函数测试 START === ');
console.log(hashFn('123')); //--> 5
console.log(hashFn('abc')); //--> 6
console.log('=== END 哈希函数测试 END === ');

const hashTable = new HashTable();

// put() 测试
hashTable.put('name', 'XPoet');
hashTable.put('age', 18);
hashTable.put('height', 178);
hashTable.put('email', 'i@xpoet.cn');
hashTable.put('address', 'china');
console.log(hashTable);
//--> {storage: Array(6), count: 5, limit: 7, loadFactor: 0.75, minLoadFactor: 0.25}

hashTable.put('address2', 'china2');
console.log(hashTable);
//--> {storage: Array(16), count: 6, limit: 17, loadFactor: 0.75, minLoadFactor: 0.25}

// get() 测试
console.log(hashTable.get('name')); //--> XPoet

// remove() 测试
hashTable.remove('address');
console.log(hashTable);
//--> {storage: Array(16), count: 5, limit: 17, loadFactor: 0.75, minLoadFactor: 0.25}


console.log('// ----- 哈希表结构测试 END -----//');



​

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值