写在前面
本文出自:代码随想录:哈希表篇
纯学习记录
哈希表
哈希表(英文名字为Hash table,国内也有一些算法书籍翻译为散列表),
是根据关键码的值而直接进行访问的数据结构。
一般来说哈希表都是用来快速判断一个元素是否出现集合里。
例如要查询一个名字是否在这所学校里。
要枚举的话时间复杂度是O(n),但如果使用哈希表的话, 只需要O(1)就可以做到。
我们只需要初始化把这所学校里学生的名字都存在哈希表里,在查询的时候通过索引直接就可以知道这位同学在不在这所学校里了。
将学生姓名映射到哈希表上就涉及到了hash function ,也就是哈希函数。
哈希函数
哈希函数,把学生的姓名直接映射为哈希表上的索引,然后就可以通过查询索引下标快速知道这位同学是否在这所学校里了。
哈希函数如下图所示,通过hashCode把名字转化为数值,一般hashcode是通过特定编码方式,可以将其他数据格式转化为不同的数值,这样就把学生名字映射为哈希表上的索引数字了。
哈希碰撞
如图所示,小李和小王都映射到了索引下标 1 的位置,这一现象叫做哈希碰撞。
一般哈希碰撞有两种解决方法, 拉链法和线性探测法。
拉链法
刚刚小李和小王在索引1的位置发生了冲突,发生冲突的元素都被存储在链表中。 这样我们就可以通过索引找到小李和小王了
(数据规模是dataSize, 哈希表的大小为tableSize)
其实拉链法就是要选择适当的哈希表的大小,这样既不会因为数组空值而浪费大量内存,也不会因为链表太长而在查找上浪费太多时间。
线性探测法
使用线性探测法,一定要保证tableSize大于dataSize。 我们需要依靠哈希表中的空位来解决碰撞问题。
例如冲突的位置,放了小李,那么就向下找一个空位放置小王的信息。所以要求tableSize一定要大于dataSize ,要不然哈希表上就没有空置的位置来存放 冲突的数据了。如图所示:
常见的三种哈希结构
当我们想使用哈希法来解决问题的时候,我们一般会选择如下三种数据结构。
- 数组
- set (集合)
- map(映射)
1.数组作为哈希表
题目限制数值的大小
不使用数组作为哈希表的情况:
数组的大小是有限的,受到系统栈空间(不是数据结构的栈)的限制。
如果数组空间够大,但哈希值比较少、特别分散、跨度非常大,使用数组就造成空间的极大浪费。
2.Set作为哈希表
题目没有限制数值的大小,就无法使用数组来做哈希表了。所以此时一样的做映射的话,就可以使用set了。
set来做哈希法的局限:
set是一个集合,里面放的元素只能是一个key,而两数之和这道题目,不仅要判断y是否存在而且还要记录y的下标位置,因为要返回x 和 y的下标。所以set 也不能用。
3.Map作为哈希表
map是一种<key, value>
的结构,可以用key保存数值,用value在保存数值所在的下标。
总结
-
当我们遇到了要快速判断一个元素是否出现集合里的时候,就要考虑哈希法。
-
但是哈希法也是牺牲了空间换取了时间,因为我们要使用额外的数组,set或者是map来存放数据,才能实现快速的查找。
-
如果在做面试题目的时候遇到需要判断一个元素是否出现过的场景也应该第一时间想到哈希法!
写在最后:新手上路,如有问题,欢迎大家指出,给意见。如果这篇文章对你有帮助,麻烦帮忙点一下赞,你们小小的举动能给我无限大的动力。拒绝白嫖,从我做起,从现在做起。Thank you for watching!