本文内容取此教程中的P55~P76,如需了解哈希表理论知识请先观看视频
以下代码采用链地址法来实现哈希表
- 实现的哈希表(基于storage的数组)每个index对应的是一个数组(bucket).(当然基于链表也可以)
- bucket中存放什么呢?我们最好将key和value都放进去,我们继续使用一个数组.(其实其他语言使用元组更好)
- 最终我们的哈希表的数据格式是这样: [[ [k,v], [k,v], [k,v]], [[k,v], [k,v], [k,v]]]
注:完整代码见最后
一、插入和修改数据
// 插入和修改
HashTable.prototype.put = function(key, value) {
// 1、根据key获取对应的index
let index = this.hashFunc(key, this.limit)
// 2、根据index获取对应的bucket(桶)
let bucket = this.storage[index]
// 3、判断bucket是否存在
if (!bucket) {
bucket = []
this.storage[index] = bucket
}
// 4、判断是否是修改数据
for (let element of bucket) {
if (element[0] === key) {
element[1] = value
return
}
}
// 5、添加操作
bucket.push([key, value])
this.count += 1
// 6、判断是否需要扩容操作
if (this.count > this.limit*0.75) {
this.resize(this.getPrime(this.limit*2))
}
}
二、获取数据
思路
- 根据key获取对应的index
- 根据index获取对应的bucket
- 判断bucket是否为null;如果为null,直接返回null
- 线性查找bucket中每一个key是否等于传入的key,如果等于,那么直接返回对应的value
- 遍历玩后,依然没有找到对应的key大 直接return null即可
HashTable.prototype.get = function(key) {
let index = this.hashFunc(key, this.limit)
let bucket = this.storage[index]
if (!bucket) {
return null
}
for (let element of bucket) {
if (element[0] === key) {
return element[1]
}
}
return null
}
三、删除数据
思路
- 根据key获取对应的index
- 根据index获取bucket
- 判断bucket是否存在,如果不存在,那么直接返回null
- 线性查找bucket,寻找对应的数据,并且删除
- 依然没有找到,那么返回nul
HashTable.prototype.remove = function(key) {
let index = this.hashFunc(key, this.limit)
let bucket = this.storage[index]
if (!bucket) return null
for (let i=0; i<bucket.length; i++) {
let tuple = bucket[i]
if (tuple[0] === key) {
bucket.splice(i, 1)
this.count -= 1
// 6、判断是否需要扩容操作
if (this.limit >7 && this.count < this.limit*0.25) {
this.resize(this.getPrime(Math.floor(this.limit/2)))
}
return tuple[1]
}
}
return null
}
四、其他方法
// 判断哈希表是否为空
HashTable.prototype.isEmpty = function() {
return this.count === 0
}
// 获取哈希表数据总数
HashTable.prototype.size = function() {
return this.count
}
// 判断是否为质数:扩容需要
HashTable.prototype.isPrime = function(num) {
for (let i=2; i<=parseInt(Math.sqrt(num)); i++){
if (num % i === 0) {
return false
}
console.log(i)
}
return true
}
五、扩容
HashTable.prototype.resize = function(newLimit) {
// 1、保存旧的数组内容
let oldStorage = this.storage
// 2、重置所有属性
this.storage = []
this.count = 0
this.limit = newLimit
// 3、遍历oldStorage中的所有bucket
for (let bucket of oldStorage) {
if (bucket) {
for (let tuple of bucket) {
this.put(tuple[0], tuple[1])
}
}
}
}
// 获取质数:为了实现容量恒为质数
HashTable.prototype.getPrime = function(num) {
while (!this.isPrime(num)) {
num++
}
return num
}
六、完整代码
function HashTable() {
// 属性
this.storage = []
this.count = 0 // 当前已存放的元素
this.limit = 7 // 当前数组总长度
// 方法
HashTable.prototype.hashFunc = function(str, size) {
let hashCode = 0
// 霍纳算法,来计算hashCode的值
for (let i=0; i<str.length; i++) {
hashCode = 37 * hashCode + str.charCodeAt(i)
}
// 取余操作
const index = hashCode % size
return index
}
// 插入和修改
HashTable.prototype.put = function(key, value) {
// 1、根据key获取对应的index
let index = this.hashFunc(key, this.limit)
// 2、根据index获取对应的bucket(桶)
let bucket = this.storage[index]
// 3、判断bucket是否存在
if (!bucket) {
bucket = []
this.storage[index] = bucket
}
// 4、判断是否是修改数据
for (let element of bucket) {
if (element[0] === key) {
element[1] = value
return
}
}
// 5、添加操作
bucket.push([key, value])
this.count += 1
// 6、判断是否需要扩容操作
if (this.count > this.limit*0.75) {
this.resize(this.getPrime(this.limit*2))
}
}
HashTable.prototype.get = function(key) {
let index = this.hashFunc(key, this.limit)
let bucket = this.storage[index]
if (!bucket) {
return null
}
for (let element of bucket) {
if (element[0] === key) {
return element[1]
}
}
return null
}
HashTable.prototype.remove = function(key) {
let index = this.hashFunc(key, this.limit)
let bucket = this.storage[index]
if (!bucket) return null
for (let i=0; i<bucket.length; i++) {
let tuple = bucket[i]
if (tuple[0] === key) {
bucket.splice(i, 1)
this.count -= 1
// 6、判断是否需要扩容操作
if (this.limit >7 && this.count < this.limit*0.25) {
this.resize(this.getPrime(Math.floor(this.limit/2)))
}
return tuple[1]
}
}
return null
}
HashTable.prototype.isEmpty = function() {
return this.count === 0
}
HashTable.prototype.size = function() {
return this.count
}
HashTable.prototype.resize = function(newLimit) {
// 1、保存旧的数组内容
let oldStorage = this.storage
// 2、重置所有属性
this.storage = []
this.count = 0
this.limit = newLimit
// 3、遍历oldStorage中的所有bucket
for (let bucket of oldStorage) {
if (bucket) {
for (let tuple of bucket) {
this.put(tuple[0], tuple[1])
}
}
}
}
HashTable.prototype.getPrime = function(num) {
while (!this.isPrime(num)) {
num++
}
return num
}
HashTable.prototype.isPrime = function(num) {
for (let i=2; i<=parseInt(Math.sqrt(num)); i++){
if (num % i === 0) {
return false
}
console.log(i)
}
return true
}
}
const ht = new HashTable()
ht.put('a', 1)
alert(ht.get('a')) // 输出: 1
ht.remove('a')
alert(ht.get('a')) // 输出: null