js封装数据结构_08_哈希表

一、哈希函数

哈希函数:函数里面封装了将字符串转化为对应的index的具体实现;

我们要将字符串转化为对应的index,首先得知道如何区分字符串,一般我们采用编码方案,也就是每一个字符都有自己对应的唯一的编码:

现成的就是unicode(万国码),unicode是字符集没有规定如何存储对应的编码。unicode的实现有UTF-16、UTF-8、UTF-32,这里我们采用UTF-16;而js也给我们封装了对应的API:

charCodeAt() //返回0·65535之间的整数,表示给定索引处的UTF-16代码单元;

有了对应的字符的编码,新的问题来了,我们要如何区分每个字符串呢?

编码相加吗?这是不行的,容易重复;

幂的连乘?这个可以的,可以基本保证唯一性,但是我们对算法做出优化,因为直接加乘效率比较低,我们可以采用霍纳算法;

还有一个问题:使用幂的连乘之后,index过于巨大,很消耗性能,我们得想办法将index缩小或者说映射到小一点的index上;如何实现了,这里可以采用取模;用于取模的除数最好是质数,可以让index的分布更均与;

以上就是哈希函数的实现思路,代码实现:

第一个参数是需要映射的字符串,第二个是哈希表的长度(用于去模)

hashTabel.prototype.hashFuns = function (str, size) {
    // 定义hashcode变量
    let hashcode = 0;

    // 类似霍纳算法
    for (let i = 0; i < str.length; i++) {
      hashcode = 37 * hashcode + str.charCodeAt(i);
    }

    // 取余(模)操作
    let index = hashcode % size;

    return index;
  };

二、index冲突的解决

那我们有了哈希函数后映射出来的index是唯一的吗?

其实不是,也会起冲突的,有两种解决办法: 

链地址法:就是存在冲突,我们就用链表或数组将他们放在一起,找的时候,遍历一下即可;

存取或探测效率随填装因子(元素总数/哈希表长度)的增大而平稳增大;

开放地址法:存在冲突,就去寻找空白的位置来放置数据;

存取或探测效率随填装因子(元素总数/哈希表长度)的增大而增大;前0.25效率很高,后0.75效率低;

这里我们采用链地址法,用数组存放冲突元素,数组的名字叫-bucket,里面的内容叫-tuple;

三、实现哈希表

有了哈希函数后,哈希表的实现就很简单了,就是创建数组,根据对应的index去数组里找bucket,再遍历bucket拿到元素或放入新元素即可;

四、具体实现

function hashTabel() {
  // 属性
  this.storage = [];
  this.count = 0;
  this.limit = 7;

  // 方法
  // 1.哈希函数
  hashTabel.prototype.hashFuns = function (str, size) {
    // 定义hashcode变量
    let hashcode = 0;

    // 霍纳算法
    for (let i = 0; i < str.length; i++) {
      hashcode = 37 * hashcode + str.charCodeAt(i);
    }

    // 取余(模)操作
    let index = hashcode % size;

    return index;
  };

  // 插入,修改操作
  hashTabel.prototype.put = function (key, value) {
    //   1.根据key,获取对应的idnex
    let index = this.hashFuns(key, this.limit);
    //   2.桶bucket
    let bucket = this.storage[index];
    if (!bucket) {
      bucket = [];
      this.storage[index] = bucket;
    }
    //   判断桶里面有没有对应的数组
    for (let index = 0; index < bucket.length; index++) {
      const tuple = bucket[index];
      if (tuple[o] == key) {
        tuple[1] = value;
        return;
      }
    }
    //    进行添加数据
    bucket.push([key, value]);

    //   别忘了count++
    this.count++;

    // 判断是否需要扩容操做
    if (this.count > this.limit * 0.75) {
      this.resize(this.limit * 2);
    }
  };

  //   获取操作
  hashTabel.prototype.get = function (key) {
    let index = this.hashFuns(key, this.limit);

    if (!this.storage[index]) return null;

    let bucket = this.storage[index];
    for (let index = 0; index < bucket.length; index++) {
      const tuple = bucket[index];
      if (tuple[0] == key) return tuple[1];
    }
    return null;
  };

  // 删除方法
  hashTabel.prototype.remove = function (key) {
    let index = this.hashFuns(key, this.limit);

    if (!this.storage[index]) return false;
    let bucket = this.storage[index];

    for (let index = 0; index < bucket.length; index++) {
      const tuple = bucket[index];
      if (tuple[0] == key) {
        bucket.splice(index, 1);
        this.count--;

        // 判断是否需要减容
        if (this.limit > 7 && this.count < this.limit * 0.25) {
          this.resize(Math.floor(this.limit / 2));
        }

        return tuple[1];
      }
    }
    return null;
  };

  // 其他方法
  hashTabel.prototype.isEmpty = function () {
    return this.count == 0;
  };

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

  hashTabel.prototype.resize = function (newLimit) {
    // 保留原数据
    let oldstorage = this.storage;

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

    // 以前数据遍历放入
    for (let index = 0; index < oldstorage.length; index++) {
      if (!oldstorage[index]) continue;
      const bucket = oldstorage[index];
      for (let i = 0; i < bucket.length; i++) {
        let tuple = bucket[i];
        this.put(tuple[0], tuple[1]);
        // this.count++;put有++
      }
    }
  };
}


对了也可以给哈希表扩容的,这里就不实现了哈,拜拜

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值