文章目录
哈希表简介JS实现哈希表完整代码参考资料
哈希表简介
基于数组实现
优势:可以快速插入-查找-删除操作
不足:
1. 数据没有顺序,不能以固定顺序遍历
2. key不允许重复
哈希函数:将key转换为index的函数,该过程称为哈希化
哈希表:使用哈希函数转换索引值并进行结构封装的数组
哈希冲突:不同的key使用哈希函数转换后的index相同(不可避免)
解决方法
1. 链地址法:index中一链表或数组的形式存放多条数据(index相同的不同key)
2. 开放地址法:寻找空白的单元格来放置冲突的数据项
+ 寻找空白单元格的方式
++ 线性查找:由得到的index,逐个往后查找(既步长为1)
++ 二次查找:index每次增加pow(i++,2)
++ 再哈希化:有一个新的哈希函数计算步长
装填因子(loadFactor):总数据项/哈希表长度
链地址法的loadFactor可能 > 1
开放地址法的loadFactor最大为1
JS实现哈希表
HashTable类的属性与方法
属性:容量-limit、数据条数-size、数据容器storege
方法:增(改)-put(key, value)、删-remove(key)、查-get(key)
哈希函数hashFunc(key):使用霍纳算法计算出index
插入数据:包括修改数据的功能 key已经存在哈希表中则修改数据,否则插入数据
根据key计算的index 查看index是否存在数据 若不存在数据,则创建数组,并插入数据 若存在数据,则遍历该数组,是否存在相等的key,存在则替换,不存在则插入
查找数据: 根据key计算的index,查找到则返回对应的值,否则返回null
删除数据 根据key计算的index,查找到则使用slice删除数据,并返回删除的值,否则返回null
扩容问题
当装载因子过大,哈希表的效率会下降,故考虑扩容(loadFactor > 0.75)
当装载因子过小,会造成空间浪费,故考虑缩容(loadFactor < 0.25)
在put操作时,当 loadFactor > 0.75 时扩容
在remove操作时,当 loadFactor < 0.25 时缩容
扩(缩)容函数resize(newLimit)
1. 将原数据指向 oldStorage
2. this.storage 指向空数组
3. 设置 limit 值为 newLimit
4. 遍历 oldStorage 将数据,执行 put 操作
注:newLimit 应该是一个质数
完整代码
function HashTable() {
this.storage
= []
this.count
= 0
this.limit
= 7
}
HashTable
.prototype
.hashFunc = function(str
, size
) {
let hashCode
= 0
for (let i
= 0; i
< str
.length
; i
++) {
hashCode
= 37 * hashCode
+ str
.charCodeAt(i
)
}
let index
= hashCode
% size
return index
}
HashTable
.prototype
.put = function(key
, value
) {
let index
= this.hashFunc(key
, this.limit
)
let bucket
= this.storage
[index
]
if (bucket
== null) {
bucket
= []
this.storage
[index
] = bucket
}
for (let i
= 0; i
< bucket
.length
; i
++) {
let tuple
= bucket
[i
];
if (tuple
[0] == key
) {
tuple
[1] = value
return
}
}
bucket
.push([key
, value
])
this.count
+= 1
if (this.count
> this.limit
* 0.75) {
let newSize
= this.limit
* 2
let newPrime
= this.getPrime(newSize
)
this.resize(newPrime
)
}
}
HashTable
.prototype
.get = function(key
) {
let index
= this.hashFunc(key
, this.limit
)
let bucket
= this.storage
[index
]
if (bucket
== null) {
return null
}
for (let i
= 0; i
< bucket
.length
; i
++) {
let tuple
= bucket
[i
];
if (tuple
[0] == key
) {
return tuple
[1]
}
}
return null
}
HashTable
.prototype
.remove = function(key
) {
let index
= this.hashFunc(key
, this.limit
)
let bucket
= this.storage
[index
]
if (bucket
== null) {
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
if (this.limit
> 7 && this.count
< this.limit
* 0.25) {
let newSize
= Math
.floor(this.limit
/ 2)
let newPrime
= this.getPrime(newSize
)
this.resize(newPrime
)
}
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
) {
let oldStorage
= this.storage
this.storage
= []
this.count
= 0
this.limit
= newLimit
for (let i
= 0; i
< oldStorage
.length
; i
++) {
const bucket
= oldStorage
[i
];
if (bucket
!= null) {
for (let j
= 0; j
< bucket
.length
; j
++) {
const tuple
= bucket
[j
];
this.put(tuple
[0], tuple
[1])
}
}
}
}
HashTable
.prototype
.isPrime = function(num
) {
if (num
<= 1) {
return false
}
for (var i
= 2; i
<= Math
.sqrt(num
); i
++) {
if (num
% i
== 0) {
return false;
}
}
return true;
}
HashTable
.prototype
.getPrime = function(num
) {
while (!this.isPrime(num
)) {
num
++
}
return num
}
let ht
= new HashTable()
ht
.put('class1','Tom')
ht
.put('class2','Mary')
ht
.put('class3','Gogo')
ht
.put('class4','Tony')
console
.log("Size: " + ht
.size());
console
.log("Limit: " + ht
.limit
);
console
.log(ht
);
参考资料
JavaScript实现哈希表