哈希表
哈希表与数组
哈希表基于数组实现,利用了数组通过索引查找元素效率极高的特点.
数组的特点
- 优点
-
- 通过〇索引查找对应的元素效率极高
-
- 缺点
-
- 由于数组是有序的,所以为了保证其有序性,在①删除或者②添加元素之后,整个数组中的元素都要进行移动,导致在插入或者删除操作上效率比较低.
-
- 数组查找效率高仅限于通过索引进行查找,如果通过元素③内容或者④内容的一部分查找索引,则效率并不高.
-
- 举例说明:
// 〇按照索引查找
const _a = [1, 2, 3, 4];
const _b = _a[2]; // 这样做效率非常高
// ①插入元素
_a.splice(0, 1); // 删除索引值为2的元素;
console.log(_a); // 删除之后_a变成[2, 3, 4]; 也就是说后面三个元素都往前挪了一位,这个效率就低了
// ②删除元素
_a.splice(2, 0, 5); // 将5插入到索引值为2的地方
console.log(_a); // 插入之后_a变成[2, 3, 5, 4]; 同删除一样,其余元素也要挪动;甚至连方法名都是一样的
// ③通过内容查找, 需要遍历整个数组
const _c = ['b', 'c', 'v']; // 找出'c'的下标
let index = 0;
_c.forEach(
(item,i) => {
i === 'c' && index = i;
}
)
// ④通过内容的一部分查找
const _d = [
{name: 'zs', age: 18},
{name: 'ls', age: 19},
{name: 'wv', age: 20},
{name: 'zl', age: 21},
];
// 现在要找出age为19的人员
let index = 0;
_d.forEach(
(item,i) => {
item.age === 19 && index = i;
}
) // 同样要遍历数组
哈希表的特点
- 相较于上述的数组的几个特点,哈希表的特点可简单总结成:
发挥优势, 抛弃不足 - 是的,哈希表并没有想办法克服缺点,而是直接抛弃了数组的劣势.
- 具体表现在:
- 哈希表不是有序的,所以在删除或者插入新的数据之后不会像数组那样挪动很多元素,所以说哈希表直接抛弃了有序性
- 发挥优势表现在:
- 哈希表通过特殊的
哈希函数实现了通过内容或者内容的一部分也能快速查找元素的功能.
- 哈希表通过特殊的
哈希函数(HashCode)
作用
将内容变换成下标值. 然后再利用数组通过下标索引速度很快的特点,间接实现内容查找或者通过部分内容查找
原理
内容可以抽象成字符串,下标是数字,所以哈希函数实现的功能简单来说就是:从字符串到数字的一个映射,难点在于:
- 不同的字符串映射结果不同,相同字符串映射结果相同
- 映射得到的数字要在一个限制的范围内
- 关于第一点: 实现要考虑效率问题,
- 关于第二点: 也是很难做到的!
难点问题解决思路
首先解决第一个问题, 然后在第一步解决的问题上再设法解决难点二.
大数变小数的基本原理
假如使用哈希表处理过'a' 'b' 'c' 'd' 'e' 'f'之后,得到的下标值为:
1, 12, 103, 1004, 10005, 100006
如果使用上述的下标,那就需要创建一个容量至少为100007的数组,但是
再次对下标映射f↓
1, 2, 3, 4, 5, 6
这个是很只需要一个容量最小为7的数组即可.
显然,这个映射下标的函数就是对10取余:
(x) => x % 10;
实际上,一般是对质数取余,以尽可能的减少重复,这个数一般是: 37
大数变小数过程中出现的下标重复问题
上面的例子是精心设计过的,所以不会出现什么问题,但如果是下面这样,就会出现下标重复的问题:
假如使用哈希表处理过'a' 'b' 'c' 'd' 'e' 'f'之后,得到的下标值为:
1, 12, 102, 1004, 10005, 100006
如果使用上述的下标,那就需要创建一个容量至少为100007的数组,但是
再次对下标映射f↓
1, 2, 2, 4, 5, 6
可以看出来,下标2重复了,数组中的下标是不能重复的,否则就会出现数据覆盖的问题.
解决下标重复问题的方法
- 链地址法
- 开放地址法
小结
- 哈希表基于数组实现,抛弃了数组的有序性,发扬了数组查询数据快的特点
- 哈希函数是哈希表的重要组成,其作用是将字符串安装一定的规则转成数字
- 将哈希函数处理得到的下标收紧的过程通常使用对质数取余
- 取余可能会产生下标重复,专有名词叫做冲突
- 解决下标冲突的方法有:链地址法和开发地址法