美文网首页
散列--哈希

散列--哈希

作者: 阔阔飞翔 | 来源:发表于2019-06-14 10:25 被阅读0次

    一、散列表:哈希表

    散列表用的是数组支持按照下标随机访问数据的特性,所以散列表其实就是数组的一种扩展,由数组演化而来。可以说,如果没有数组,就没有散列表。时间复杂度是O(1)

    二、哈希算法

    (1)从哈希值不能反向推导出原始数据(所以哈希算法也叫单向哈希算法);

    (2)对输入数据非常敏感,哪怕原始数据只修改了一个Bit,最后得到的哈希值也大不相同;

    (3)散列冲突的概率要很小,对于不同的原始数据,哈希值相同的概率非常小;

    (4)哈希算法的执行效率要尽量高效,针对较长的文本,也能快速地计算出哈希值。

    哈希算法的应用非常非常多,分别是安全加密(MD5,DES等)、唯一标识、数据校验、散列函数、负载均衡、数据分片、分布式存储。

    三、哈希冲突(也叫散列冲突)

    比如:数组的长度是5。这时有一个数据是6。那么如何把这个6存放到长度只有5的数组中呢。按照取模法,计算6%5,结果是1,那么就把6放到数组下标是1的位置。那么,7就应该放到2这个位置。到此位置,哈斯冲突还没有出现。这时,有个数据是11,按照取模法,11%5=1,也等于1。那么原来数组下标是1的地方已经有数了,是6。这时又计算出1这个位置,那么数组1这个位置,就必须储存两个数了。这时,就叫哈希冲突。

    解决方法:开放寻址法和链表法

    1、开放寻址法:

    线性探测:当我们往散列表中插入数据时,如果某个数据经过散列函数散列之后,存储位置已经被占用了,我们就从当前位置开始,依次往后查找,看是否有空闲位置,直到找到为止。

    二次探测:跟线性探测很像,线性探测每次探测的步长是1,那它探测的下标序列就是hash(key)+0,hash(key)+1,hash(key)+2……而二次探测探测的步长就变成了原来的“二次方”,也就是说,它探测的下标序列就是hash(key)+0,hash(key)+12,hash(key)+22……

    双重散列:意思就是不仅要使用一个散列函数。我们使用一组散列函数hash1(key),hash2(key),hash3(key)……我们先用第一个散列函数,如果计算得到的存储位置已经被占用,再用第二个散列函数,依次类推,直到找到空闲的存储位置。

    不管采用哪种探测方法,当散列表中空闲位置不多的时候,散列冲突的概率就会大大提高。为了尽可能保证散列表的操作效率,一般情况下,我们会尽可能保证散列表中有一定比例的空闲槽位。我们用装载因子(load factor)来表示空位的多少。

    装载因子的计算公式是:

    散列表的装载因子=填入表中的元素个数/散列表的长度。

    装载因子越大,说明空闲位置越少,冲突越多,散列表的性能会下降。

    2.链表法

    链表法是一种更加常用的散列冲突解决办法,相比开放寻址法,它要简单很多。我们来看这个图,在散列表中,每个“桶(bucket)”或者“槽(slot)”会对应一条链表,所有散列值相同的元素我们都放到相同槽位对应的链表中。

    当插入的时候,我们只需要通过散列函数计算出对应的散列槽位,将其插入到对应链表中即可,所以插入的时间复杂度是O(1)。当查找、删除一个元素时,我们同样通过散列函数计算出对应的槽,然后遍历链表查找或者删除。那查找或删除操作的时间复杂度是多少呢?

    实际上,这两个操作的时间复杂度跟链表的长度k成正比,也就是O(k)。对于散列比较均匀的散列函数来说,理论上讲,k=n/m,其中n表示散列中数据的总个数,m表示散列表中“槽”的个数。

    相关文章

      网友评论

          本文标题:散列--哈希

          本文链接:https://www.haomeiwen.com/subject/gagqfctx.html