哈希算法

哈希表,又称为散列表,是一种数据结构,大体概念估计大家都清楚,我这里不在赘述。

目的:用来查询。 通过给定关键字—> 直接找到数据的内存位置(也就是说直接此数据)。 (是不是和 key-value 很像)

方式: 通过计算一个键值的函数,将所需要查询的 数据映射到表中的一个位置来访问记录,这样子加快了查找速度。 这个函数称为散列函数,存放记录的数组称为散列表

有两点很重要: 先撇开哈希算法,我们应该思考一下,如何根据一个key,直接定位数据在内存的位置呢? 可以使用一张表,第一列存储key,第二列存储这个key在内存中的位置。
如果这个表采用数组进行存储,那么查询这个表的时间复杂度是O(n)。
如果这个表采用二叉树进行存储(假设关键字可以比较大小),那么查询这个表需要O(logn)的时间复杂度

这里我们需要的真的数据不是在一起的(通过key->内存位置->访问数据)。
现在呢?我们让所有的数据都存放在一个线性的数组中,那么内存位置即为数组下标。 我们只需要关系如何通过 key——>数组下标。
哈希算法采用 直接使用数学函数计算的方式 f(key)=下标。

最大的问题:如果不同的关键字 被 哈希之后,得到了同样的 下标 怎么办? 这个称谓冲突
第二个问题:哈希函数 怎么设定呢?

以下全篇,我们之讲解这两个问题!


处理冲突

冲突不可避免,必须处理(不产生冲突的可能性非常小)。 那么应该如何处理呢?

开放定址法

hash = (hask(key) + d(i)) mod m , i = 1, 2, …, k (k <= m - 1)。其中 d(i) 为增量序列,i是已经发生的碰撞的次数。 增量序列有如下取法
1. 线性探测 d = 1, 2, 3, … (m-1)
2. 平方探测。相对于线性探测,相当于发生碰撞的时候,探测时间间隔 平方个单位的位置是否为空。如果为空,将地址存放进去。
3. 伪随机数 探测。

这里表的大小很重要。 同时,模数p的取值很重要,越是质数,那么 mod 取余就越可能分布在表的各处。

这个有可能形成聚集(cluster): 在函数地址的表中,散列函数的结果不均匀占据表的单元,形成区块。插入到区块中的关键字需要多次试选单元才能够插入到表中,这样造成时间浪费。对于开放定址法,聚集是灾难性的,必须避免。

单独链表法

将散列到同一个存储位置的所有元素保存在同一个链表中。

再次散列

可以用多个散列函数,进行散列,直到碰撞不再发生。这种方法不容易产生聚集。但是增加了计算时间

建立一个公共溢出区

将所有产生冲突的都放在这个溢出区中。


哈希函数

如果对于关键字集合中的任意一个关键字,经过散列函数 映射到 地址集合中任意一个 地址上的概率是相等的话, 那么称这个散列函数为”均匀散列函数(Uniform Hash Function)“
均匀散列函数:这样可使使得关键字经过散列函数得到一个”随机的地址“,从而减少了碰撞。
所以一个好的散列函数,可以使得数据序列的访问过程更加有效,可以减少冲突。

  • 直接定址法:取关键字或者关键字的某个线性函数值为散列地址: hash(k) = k 或者 hash(k) = a * k + b
  • 数字分析法: 假设关键字是 以 r 为基数的数字,并且哈希表中可能出现的关键字都是事先知道的,那么可以取关键字的若干树位组成哈希地址。:
    • 举例子:一个班同学的生日,有年月日,经过分析,年月重复的可能性比较大,那么就选取日来作为哈希。
  • 平方取中法: 取关键字的平方后的中间几位为哈希地址。 (通常在选取哈希函数的时候,不一定可以知道关键字的全部情况,取其中的哪几位也不一定合适,而一个数的平方后的中间几位数字和数的每一位都相关,由此使随机分布的关键字得到的哈希地址也是随机的。取的位数由表长决定)
  • 折叠法: 将关键字分割成位数相同的几个部分(最后一个部分的位数可以不相同),然后取这几个部分的叠加和(舍去进位) 作为哈希地址。
  • 除留余数法: 取关键字被某个不大于散列表表长m的数p除后所得到的余数为散列地址。 hash(k) = k mod p , p <= m。 不仅可以对关键字直接取模数,也可以在折叠法,平方取中法等运算之后取模数。对p的选择很重要,一般来说取素数或者 m,若p选择不好,容易产生碰撞。

    坏消息:一个好的哈希函数比较困难,需要一定的技术含量。
    好消息:现在又很多现成的不错的哈希函数,可以直接使用。


查找效率

可以参看:维基百科
这里有一个载荷因子的定义很关键。
散列表的载荷因子:a = (填入表中的元素的个数) / (散列表的长度)

a是散列表装满程度的标志因子。 由于表长是一个定值,a 与“填入表中的元素个数”成正比,所以a越大,表明填入的元素越多,产生冲突的可能性越大。实际上散列表的平均查找长度即为 载荷因子的a的函数,知识不同处理冲突的方法有不同的函数。

对于开放定址法,载荷因子是特别重要的因素,应该严格限制在 0.7~0.8一下。超过0.8,查表的时候CPU的缓存不命中,按照指数曲线上升。因此一些开放定址法的hash库,如JAVa的系统库,限制了载荷因子为0.75。超过这个值,将resize 散列表。


全域哈希 和 完美哈希

http://www.guokr.com/blog/483599/
http://www.cnblogs.com/a180285/archive/2012/07/22/opmphf.html
http://www.yankay.com/introduction-to-opmphf/
http://blog.csdn.net/chixinmuzi/article/details/1727195


一致性 hash 算法

http://blog.chinaunix.net/uid-29815859-id-4428253.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值