[Data Structure & Algorithm] 哈希表
哈希方法(Hashing) - 散列
- 通过函数h将K映射到表T[0..M-1]的下标上,这样h(Ki)就是对应结点Ki的存储地址
- 哈希表 - T
- 哈希表长 - M
- 哈希函数 - h
- 冲突(Collision) - 两个不同的关键字,通过哈希函数映射到同一个地址
- 优点 - 直接寻址查找,理想时间复杂度为O(1),但实际很难做到
- 装填因子α = 哈希表中关键字的个数/哈希表的长度
- α越小 -> 哈希表中的空地址越多 -> 发生冲突的可能性越小 -> 平均查找实践越小
- 查询成功平均长度 = 查询成功的次数/关键字的个数
- 如果没有冲突,则查询成功的次数 = 1
- 如果为了解决而偏移过地址,则查询成功的次数 = 偏移量
- 查询失败平均长度 = 查询失败的次数/关键字的个数
- 如果是空地址,则查询失败的次数 = 1
- 如果不是空地址,则查询失败的次数 = 从这个地址到最近的空地址的距离
哈希函数的构造
-
要求
- 尽量减少冲突
- 哈希后的地址随机分布在整个地址区间
- 以下方法可以搭配使用
-
直接定址法
- 通过线性计算, 如h(key) = a*key + b
- 要求 - 关键字分布具有很强的规律性
- 优点 - 不会产生冲突
-
数据分析法
- 选择随机性较强的部分作为地址,如对于8位数的集合,选择其中随机性较强的几位数,尽量构成不相同的地址
- 要求
- 关键字比较长
- 关键字的形式已经确定
-
平方取中法 - 较为常用
- 先将关键字平方,再选择中间几位数作为地址
- 优点 - 平方后保证关键字的每一位都有作用,减小冲突的可能性
-
折叠法
- 移位折叠法 - 将关键词分成几部分(每部分的位数可以不同),再相加
- 边界折叠法
-
除留余数法
- 将关键字除以某个不大于哈希表表长m的数p后,取余数
- p的选取
- 一般设为小于哈希表长度的最大质数
- 一般不设为2的次幂 - 增加冲突的可能性
解决冲突的办法
- 开放定址法
- 基本思路 - 对于冲突的两个关键字,将后一个关键字通过探测方法填入另一个地址中
- 常用探测方法
- 线性探测法 - 从已经被占用的地址i开始依次向下(i+1,i+2,i+3)探测,直到找到空的地址
- 平方探测法 - 从已经被占用的地址i开始按平方数向下(i+1,i+4,i+9)探测,直到找到空的地址
- 缺点 - 不易探测到整个散列空间
- 双散列 - 有两个哈希函数h1, h2,当h1计算出的哈希值有冲突时,通过h2计算出的探测地址的增量h2(key),即从已经被占用的地址i开始从i+h2(key),i+2h2(key),i+3h2(key)探测,直到找到空的地址
- 要求 - h2的值和M互素,为了使关键字较均匀地分布在整个哈希表中
- 平均查找长度 - 1/2*(1+1/(1-α))
- 链地址法
- 基本思路
- 将哈希表的每一个地址空间都定义为一个单链表的表头指针
- 将对应相同哈希地址的关键字,链接在该地址的表头指针后
- 优点
- 处理冲突简单,平均查找时间较短
- 删除结点简单
- 不会溢出
- 缺点 - 维护指针需要额外的空间
- 适用
- 无法确定长度
- 结点规模较大
- 平均查找长度 - 1+α/2
- 基本思路