哈希表
基于数组实现,
优点:
比数组更快的插入和删除查找,比数还快。
哈希表中数据是没有顺序的。
通常情况下,key是不允许重复的
难点1:字母转数字方案,保证不重复
方案一:数字相加(数字太小,很容易重复,不合理)
自己设计一个编码譬如a:1,b:2,c:3...
cats转换成数字就是3+1+20+19=43;43作为单词下标存储在数组中
方案二:幂的连乘(基本保持一致,空间很浪费)
cats转换成数字327^3+127^2+2027^1+1927^0
哈希化:将大数字转化成数组范围内下标的过程。
哈希函数:将单词转成大数字,大数字在进行哈希化的代码实现放在一个函数中
哈希表:吧数据插入到这个数组,对整个结构的封装。
解决重复问题
主要有两种方法 1、链地址法(拉链法)装载因子可能大于1 (线性) java hashmap 2、开放地址法 装载因子1 1、链地址法解决冲突 图片: uploader.shimo.im/f/R8OTbmCDI…
2、开放地址法 产生冲突,寻找空白的单元格来存放数据。会出现数据的聚集,降低哈希的效率。
哈希表不仅仅要存删找还要扩容,扩容保证哈希的效率,getPrime保证扩容的数字是质数,质数可以让数据分布的更加均匀
if (this.count > this.limit * 0.75) {
var primeNum = this.getPrime(this.limit * 2)
this.resize(primeNum)
}
// 创建HashTable构造函数
function HashTable() {
// 定义属性
this.storage = []
this.count = 0
this.limit = 8
// 定义相关方法
// 判断是否是质数
HashTable.prototype.isPrime = function (num) {
var temp = parseInt(Math.sqrt(num))
// 2.循环判断
for (var i = 2; i <= temp; i++) {
if (num % i == 0) {
return false
}
}
return true
}
// 获取质数
HashTable.prototype.getPrime = function (num) {
while (!isPrime(num)) {
num++
}
return num
}
// 哈希函数
HashTable.prototype.hashFunc = function(str, max) {
// 1.初始化hashCode的值
var hashCode = 0
// 2.霍纳算法, 来计算hashCode的数值
for (var i = 0; i < str.length; i++) {
hashCode = 37 * hashCode + str.charCodeAt(i)
}
// 3.取模运算
hashCode = hashCode % max
return hashCode
}
// 插入数据方法
HashTable.prototype.put = function (key, value) {
// 1.获取key对应的index
var index = this.hashFunc(key, this.limit)
// 2.取出数组(也可以使用链表)
// 数组中放置数据的方式: [[ [k,v], [k,v], [k,v] ] , [ [k,v], [k,v] ] [ [k,v] ] ]
var bucket = this.storage[index]
// 3.判断这个数组是否存在
if (bucket === undefined) {
// 3.1创建桶
bucket = []
this.storage[index] = bucket
}
// 4.判断是新增还是修改原来的值.
var override = false
for (var i = 0; i < bucket.length; i++) {
var tuple = bucket[i]
if (tuple[0] === key) {
tuple[1] = value
override = true
}
}
// 5.如果是新增, 前一步没有覆盖
if (!override) {
bucket.push([key, value])
this.count++
if (this.count > this.limit * 0.75) {
var primeNum = this.getPrime(this.limit * 2)
this.resize(primeNum)
}
}
}
// 获取存放的数据
HashTable.prototype.get = function (key) {
// 1.获取key对应的index
var index = this.hashFunc(key, this.limit)
// 2.获取对应的bucket
var bucket = this.storage[index]
// 3.如果bucket为null, 那么说明这个位置没有数据
if (bucket == null) {
return null
}
// 4.有bucket, 判断是否有对应的key
for (var i = 0; i < bucket.length; i++) {
var tuple = bucket[i]
if (tuple[0] === key) {
return tuple[1]
}
}
// 5.没有找到, return null
return null
}
// 删除数据
HashTable.prototype.remove = function (key) {
// 1.获取key对应的index
var index = this.hashFunc(key, this.limit)
// 2.获取对应的bucket
var bucket = this.storage[index]
// 3.判断同是否为null, 为null则说明没有对应的数据
if (bucket == null) {
return null
}
// 4.遍历bucket, 寻找对应的数据
for (var i = 0; i < bucket.length; i++) {
var tuple = bucket[i]
if (tuple[0] === key) {
bucket.splice(i, 1)
this.count--
// 缩小数组的容量
if (this.limit > 7 && this.count < this.limit * 0.25) {
var primeNum = this.getPrime(Math.floor(this.limit / 2))
this.resize(primeNum)
}
}
return tuple[1]
}
// 5.来到该位置, 说明没有对应的数据, 那么返回null
return null
}
// isEmpty方法
HashTable.prototype.isEmpty = function () {
return this.count == 0
}
// size方法
HashTable.prototype.size = function () {
return this.count
}
// 哈希表扩容
HashTable.prototype.resize = function (newLimit) {
// 1.保存旧的数组内容
var oldStorage = this.storage
// 2.重置属性
this.limit = newLimit
this.count = 0
this.storage = []
// 3.遍历旧数组中的所有数据项, 并且重新插入到哈希表中
oldStorage.forEach(function (bucket) {
// 1.bucket为null, 说明这里面没有数据
if (bucket == null) {
return
}
// 2.bucket中有数据, 那么将里面的数据重新哈希化插入
for (var i = 0; i < bucket.length; i++) {
var tuple = bucket[i]
this.put(tuple[0], tuple[1])
}
}).bind(this)
}
}
// 测试哈希表
// 1.创建哈希表
var ht = new HashTable()
// 2.插入数据
ht.put("abc", "123")
ht.put("cba", "321")
ht.put("nba", "521")
ht.put("mba", "520")
// 3.获取数据
alert(ht.get("abc"))
ht.put("abc", "111")
alert(ht.get("abc"))
// 4.删除数据
alert(ht.remove("abc"))
alert(ht.get("abc"))