哈希表的原理与实现
哈希表
原理
哈希表(Hash table)又称散列表,是一种可以通过“关键码”(key)直接进行访问的数据结构。
哈希表由两部分组成
一个数据结构,通常是链表、数组
Hash 函数,输入“关键码”( key) ,返回数据结构的索引
介绍
- 对外表现为可以通过关键码直接访问: hash_table[key] = value,实际上是在数据结构的hash(key)位置处存储了value: data_structure[hash(key)]=value
- 最简单的例子(count()函数),关键码是整数,定义 hash(key) = key,那这个哈希表其实就是一个数组了, key自己就是下标
- 当然,一般情况下,关键码key是一个比较复杂的信息,比如很大的数、字符串这时候key就不能直接作为数据结构的下标了
- 此时就需要设计一个Hash函数,把复杂信息映射到一个较小的值域内,作为索引
例子
一个简单的 hash_table["lies"]= 233的例子,以各字符ASCIl码相加mod 20为Hash 函数
哈希碰撞
原理
哈希碰撞(Collisions)指的是两个不同的key被计算出同样的 Hash结果
把复杂信息映射到小的值域,发生碰撞是不可避免的
好的Hash函数可以减少碰撞发生的几率,让数据尽可能地均衡分布
哈希碰撞+开散列
开散列是最常见的碰撞解决方案
-
Hash 函数依然用于计算数组下标
-
数组的每个位置存储一个链表的表头指针(我们称它为表头数组)
-
每个链表保存具有同样Hash值的数据
形象描述:“挂链”——表头数组每个位置“挂”着一个链表
工程应用
- 电话号码簿
- 用户信息表
- 缓存( LRU Cache)
- 键值对存储( Redis )
完整结构图
时间复杂度
- 期望: 插入、查询、删除O(1)
-
- 数据分布比较均衡时
- 最坏: 插入、查询、删除O(n)
-
- 数据全部被映射为相同的Hash值时
无序集合、映射的实现与应用
集合与映射
- 集合( set)存储不重复的元素
-
- 有序集合,遍历时按元素大小排列,一般用平衡二叉搜索树实现,O(logN)
- 无序集合,一般用hash实现,O(1)
- 映射( map)存储关键码(key)不重复的键值对( key-value pair)
-
- 有序集合,遍历时按照key大小排列,一般用平衡二叉搜索树实现,O(logN)
- 无序集合,一般用哈希表实现,O(1)
对于语言内置的类型( int, string),已经有默认的优秀的hash 函数,可以直接放进set/map里使用
Python code
list_a = list([1, 2, 3, 4])
集合
set_a = {'peiqi', 'qioazhi', 'suxi'}
set_b = set(list_a)
字典
map_a = {'peiqi': 100, 'qiaozhi': 50, 'suxi': 30}
Cache
缓存的两个要素:大小、替换策略
常见替换算法:
-
LRU - least recently used,最近最少使用(淘汰最旧数据)
-
LFU - least frequently used,最不经常使用(淘汰频次最少数据)