理论基础
- 存储实现:
(哈希表的底层是数组)
实现哈希表的两种方法:
1、数组+链表
2、数组+红黑二叉树 - 主要用途:快速判断一个元素是否出现在集合中
- 查找时间复杂度:一般是O(1),极端情况下哈希碰撞造成的O(n)
hash表一般都会将要查找的值存在哈希表内,查询的时候直接通过引索查询即可
将值映射到哈希表上的这个过程就 使用到了 哈希函数
哈希函数
- 一般通过 hashCode 将 值 转化为引索存入 ,即将 值(传入的key) 映射到hash table 上
- 如果 值 的总数比 hash table 的size 要大,那么再怎么经过 hashCode 处理,不同的值也会使用相同的索引,此时就产生了 哈希碰撞
哈希碰撞
- 一般解决哈希碰撞有两种方法
拉链法
- 在每个hash table 的每个索引处, 存放一个链表的地址,当发生 哈希碰撞 时,就将冲突的元素全部放入链表中,这样就可以通过 引索 + 遍历链表 找到目标元素了
- 拉链法主要是需要选择适当的哈希表大小,这样既不会因为数组空值太多而浪费空间,也不会因为链表太长而在查找上消耗太多时间。
线性探测法
- 前提是 tableSize > dataSize,也就是将产生哈希碰撞的值存入 哈希表的空位中
实现哈希表的数据结构
一般通过三种方式:
- 数组
- set(集合)
- map(映射)
Java中:
- HashSet 基本使用 HashMap 来实现,key 存值,value 存一个虚拟的Object对象定义为static final
- HashMap 实现:数组 + 链表
三种结构的使用情景
- 数组:使用数组作为hash表,好处是空间小,简单有效。
例如:383. 赎金信中要求只有小写字母,那么使用数组就很合适 - set:使用set比数组好的点
- 数组的大小是有限的,受到系统栈空间的限制
- 如果数组空间足够大,但哈希值比较少,比较分散,那么数组造成非常多的空间浪费
例如题目:202. 快乐数
- map:使用map的情况是为了存储两个先关联的值,这一点set做不到
例如:1. 两数之和中可以用key存储值,value存储值对应的下标
454. 四数相加 II中用key存 A+B 值,value 存 A+B 的出现次数
总结
- 当我们需要快速判断一个元素是否出现在集合中时,就要考虑哈希法
是一种 牺牲空间 换取 时间 的方法 - 当需要存储 比较多的元素 同时 还需要 小于线性时间查找对应元素,考虑HashSet
- 当需要对应存储 值 与 值出现的次数 或者 值 与 值的引索 时考虑使用HashMap
参考
Carl:https://mp.weixin.qq.com/s/1s91yXtarL-PkX07BfnwLg
samniwu:https://blog.csdn.net/samniwu/article/details/90550196
隐者自怡悦:https://blog.csdn.net/YYQ_QYY/article/details/105992427