我们采用链地址法来实现哈希表
实现的哈希表(基于storage的数组)每个Index对应的是一个数组(bucket). 当然基于链表也可以
bucket中存放什么呢? 我们最好将key(类似单词/字符串,比如员工的名字,’Python’…)和value(解释,详情)都放进去,我们继续使用一个数组。(其实其他语言使用元组更好)
最终我们的哈希表的数据格式是这样的: [[[k,v], [k,v]], [[k,v], [k,v], [k,v]]]
代码解析:
我们定义三个属性:
- storage作为我们的数组,数组中存放相关的元素
- count表示当前已经存放了多少数据
- limit用于标记数组中一共可以存放多少个元素
插入和修改数据
哈希表的插入和修改操作是同一个函数:
因为使用者传入一个 <key, value>
时
(1)如果原来不存在该key,那么就是插入操作
(2)如果已经存在该key, 那么就是修改操作
代码解析:
步骤一:根据传入的Key获取对应的index->目的:将我们的数据插入到对应的位置
步骤二:根据索引值取出bucket(桶)->如果桶不存在,创建桶,并且放置在该索引的位置
步骤三:判断新增还是修改原来的值->如果已经有值了,那么就修改值->如果没有,执行后续的添加操作
步骤四:新增操作
步骤五:判断是否需要扩容操作(新增元素之后判断是否是否需要扩容)
获取方法
步骤一:根据key获取index
步骤二:根据index获取对应的bucket
步骤三:判断bucket是否为null—->如果为null, 那么直接返回null
步骤四:线性查找bucket中每一个key是否等于传入的Key—->如果等于,那么直接返回对应的value
步骤五:遍历完后,依然没有找到对应的key—->直接返回null
删除方法
步骤一:根据key获取对应的index
步骤二:根据Index获取对应的bucket
步骤三:判断bucket是否为null—->如果为null, 那么直接返回null
步骤四:线性查找bucket中每一个key是否等于传入的Key—>如果等于,那么删除 –>删除完之后判断是否需要缩小容量
步骤五:遍历完后,依然没有找到对应的key—>直接返回null
哈希表的扩容
为什么需要扩容?
(1)目前,我们是将所有的数据项放在长度为7的数组中
(2)因为我们使用的是链地址法,loadFactor可以大于1,所以这个哈希表可以无限制插入新数据
(3)但是,随着数据量的增多,每一个index对应的bucket会越来越长,也就造成效率的降低
(4)所以,在合适的情况对数组进行扩容。比如扩容两倍
如何进行扩容?
(1)扩容可以简单的将容量增大两倍(不是质数吗?质数的问题后面再讨论)
(2)但是这种情况下,所有的数据项一定要同时进行修改(重新调用哈希函数,来获取到不同的位置)
(3)比如hashCode=12的数据项,在length=8的时候,Index=4,在长度等于16的时候,Index=12
(4)这是一个耗时的过程,但是如果数组需要扩容,那么这个过程是必要的
什么情况进行扩容?
比较常见的情况是loadFactor>0.75的时候进行扩容
比如java的哈希表就是在装填因子大于0.75的时候,对哈希表进行扩容
哈希表节点类型
1 | // 哈希表中键对应的值 |
封装哈希表类
1 | import { HashNode, HashNodeList } from './types'; |
测试
1 | import HashTable from "./HashTable"; |