数据结构与算法学习——哈希表(下)

代码拓虹客
• 阅读 1726

引言

上面的一篇文章已经为这篇文章做了很好的铺垫,让我们认识了哈希表。那么我们来认识一下什么是扩容。扩容顾名思义就是扩大容量的意思。我们下面会封装3个方法包括,put(新增/修改数据),get(获取数据),remove(删除数据)。上面我们已经提到了先定义一个固定长度的数组。既然是固定长度那么当我们在不知道增加多少的情况下这时候就用到了扩容。本篇文章是基于链地址法来实现的哈希表。
数据结构与算法学习——哈希表(下)

什么情况下扩容呢?
比如常见的情况是loadFactor(填充因子)>0.75的时候,对哈希表进行扩容

为什么需要扩容?
目前,我们是将所有的数据项放在长度为7的数组中
因为我们使用的是链地址法,loadFactor(填充因子)可以大于1,所以这个哈希表可以无限制的插入新数据
但是,随着数据量的增多,每一个index对应的bucket会越来越长,也就造成效率的降低
所以在合适的情况对数组进行扩容,比如扩容两倍

如何扩容?
扩容可以将简单的将容量增大两倍(不是质数吗?质数问题后面在讨论)
但是这种情况下,所有的数据项一定要同时进行修改(重新调用哈希函数,来获取到不同的位置)
比如hashCode=12的数据项,在length=8的时候,index=4 在长度为16的时候呢?index=12
这是一个耗时的过程,但是如果数组需要扩容,那么这个过程是必要的

检测质数

在写方法之前我们需要用到检测质数的方法,这样可以用于数组扩容之后的长度为质数从而解决元素的聚集问题;
那么我们先来看一看普通检测

function isPrimeHigh(num) {
  for (let i = 2; i < num; i++) {
    if (num % i === 0) {
      return false
 }
  }
  return true
}

上面这种方法是可以正常检测的,但是他的效率很慢,如果是一个比较小的数还是很好的,但是如果传进去的是一个大的数字那么就很慢了。

高效的检测质数方法

//利用开方的方法 例如以下的12
//  2 * 6 = 12
//  3 * 4 = 12
//  Math.sqrt(num)*Math.sqrt(num)=12
//  4 * 3 = 12
//  6 * 2 = 12
// 循环时只需循环到Math.sqrt(num)(这里通过parseInt函数取得的是3),因为Math.sqrt(num)后面的乘法就相当于Math.sqrt(num)前面反过来一样,所以如果前面的不能被整除后面的一定不能被整除
/* 拿13举例:parseInt(Math.sqrt(13)) = 3;
 i=2时 13 % 2 = 1;
 i=3时 13 % 3 = 1; 上面的流程没有进到方法,所以返回true*/
function isPrime(num) {
  let temp = parseInt(Math.sqrt(num));
  for (let i = 2; i <= temp; i++) {
    // i=2   12 % 2 = 0 那么直接返回false
 if (num % i == 0) {
      return false
 }
  }
  return true
}

哈希表方法

数据结构与算法学习——哈希表(下)
删除方法和上图逻辑大体一致

class HashTable {
  constructor() {
    this.storage = [];
    this.count = 0;
    this.limit = 7;
  }
  //获取关键字对应的索引
 hashCode(str, size) {
    let hashCode = 0;
    //秦九韶算法根据传递进来的key值的每个字符当做关键字传进来这样减少冲突
 for (let i = 0; i < str.length; i++) {
      //37是一个常用的质数
 hashCode = hashCode * 37 + str[i].charCodeAt()
    }
    // 将一个比较大的数压缩为比较小的数字作为索引
 return hashCode % size
  }
  //新增/修改方法
 put(key, val) {
    let index = this.hashCode(key, this.limit);
    let bucket = this.storage[index];
    if (bucket && bucket.length > 0) {
      for (let i = 0; i < bucket.length; i++) {
        let tuple = bucket[i];
        if (tuple[0] === key) {
          tuple[1] = val;
          return
 }
      }
    } else {
      bucket = [];
      this.storage[index] = bucket;
    }
    bucket.push([key, val]);
    this.count += 1;
    if (this.count / this.limit > 0.75) {
      this.expansion(Math.floor(this.limit * 2))
    }
  }
  //获取元素
 get(key) {
    let index = this.hashCode(key, this.limit);
    let bucket = this.storage[index];
    if (bucket && bucket.length > 0) {
      for (let i = 0; i < bucket.length; i++) {
        let tuple = bucket[i];
        if (tuple[0] === key) {
          return tuple[1]
        }
      }
    } else {
      return null;
    }
  }
  //删除元素
 remove(key) {
    let index = this.hashCode(key, this.limit);
    let bucket = this.storage[index];
    if (bucket && bucket.length > 0) {
      for (let i = 0; i < bucket.length; i++) {
        let tuple = bucket[i];
        if (tuple[0] === key) {
          let currentVal = tuple[1];
          bucket.splice(i, 1);
          this.count -= 1;
          if (this.count / this.limit < 0.75 && this.limit > 7) {
            this.expansion(Math.floor(this.limit / 2))
          }
          return currentVal;
        }
      }
    } else {
      return null;
    }
  }
  //扩容以及将定义的数组长度转成质数
 expansion(newSize) {
    let oldStorage = this.storage;
    this.storage = [];
    this.count = 0;
    this.limit = newSize;
    while (!this.isPrime(this.limit)) {
      this.limit += 1;
    }
    for (let i = 0; i < oldStorage.length; i++) {
      let bucket = oldStorage[i];
      if (!bucket || bucket.length < 1) continue;
      for (let j = 0; j < bucket.length; j++) {
        let tuple = bucket[j];
        this.put(tuple[0], tuple[1])
      }
    }
  }
  // 判断是否是一个质数
 isPrime(num) {
    let power = Math.sqrt(num);
    for (let i = 2; i <= power; i++) {
      if (num % i === 0) {
        return false
 }
    }
    return true
 }
}
点赞
收藏
评论区
推荐文章
blmius blmius
4年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
Wesley13 Wesley13
4年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
1年前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Wesley13 Wesley13
4年前
MySQL索引初探
一、什么是索引?帮助数据库系统实现高效获取数据的数据结构索引可以帮助我们快速地定位到数据而不需要每次搜索的时候都遍历数据库中的每一行。二、常见实现方式有哪些?常见索引模型有三种:哈希表、有序数组、搜索树1.哈希表(1)使用哈希表实现的索引称为哈希索引。!(https://os
贾蔷 贾蔷
8个月前
手把手教你实现哈希表:从代码到原理的新手友好指南
一、简介和应用哈希表(HashTable)是一种高效的数据结构,通过哈希函数将键(Key)映射到存储位置,实现O(1)时间复杂度的查找、插入和删除操作。它广泛应用于缓存系统、数据库索引、字典查询等场景。例如,在编程中需要快速根据用户ID查找信息时,哈希表能
V-275670029 V-275670029
3年前
哈希竞猜游戏是什么
区块链中的哈希到底是什么?  什么是哈希?  哈希是将任意长的输入编程加密的固定长度输出的过程。哈希并不等同于加密方法,因为无法解密哈希值来获取原始数据。事实上哈希是一种单向加密函数。  Withthehashfunction,thedataontheInternetcanbesavedintheformofafixedle
V-275670029 V-275670029
3年前
哈希竞猜游戏开发搭建包网
区块链中的哈希到底是什么?  什么是哈希?  哈希是将任意长的输入编程加密的固定长度输出的过程。哈希并不等同于加密方法,因为无法解密哈希值来获取原始数据。事实上哈希是一种单向加密函数。  Withthehashfunction,thedataontheInternetcanbesavedintheformofafixedle
搭建平台吧 搭建平台吧
3年前
哈希竞猜搭建部署方案
首先,什么是哈希?哈希是将任意长的输入编程加密的固定长度输出的过程。哈希并不等同于加密方法,因为无法解密哈希值来获取原始数据。事实上哈希是一种单向加密函数。有了哈希函数,就可以将互联网上的数据以固定长度字符串的形式来保存。其中一种方法就是SHA256(安全哈希算法256位),SHA256是SHA1的后继者,SHA1的输出是160位的。哈希游戏的亮点:100%
为什么mysql不推荐使用雪花ID作为主键
作者:毛辰飞背景在mysql中设计表的时候,mysql官方推荐不要使用uuid或者不连续不重复的雪花id(long形且唯一),而是推荐连续自增的主键id,官方的推荐是auto_increment,那么为什么不建议采用uuid,使用uuid究