zoukankan      html  css  js  c++  java
  • hash冲突的解决方法

    先简单了解下哈希函数和哈希冲突的概念。

    非哈希表的特点:关键字在表中的位置和它之间不存在一个确定的关系,查找的过程为给定值一次和各个关键字进行比较,查找的效率取决于和给定值进行比较的次数。

    哈希表的特点:关键字在表中位置和它之间存在一种确定的关系。

    哈希函数:在元素关键字k和位置p之间建立一种映射关系f,使得f(k) = p;创建哈希表时,通过哈希函数将元素k存在地址为f(k)的位置;查找元素也是通过哈希函数找到元素的存放位置,然后取出值;

    哈希冲突:关键字的集合很多的时候,就有可能将两个关键字k1,k2的哈希函数计算结果相等,k1和k2的值肯定不能存放在同一个位置,就产生了哈希冲突;哈希冲突是不可避免的,只能尽量减少哈希冲突;

    冲突的解决办法有三种:

    1>开放定址法或者叫再散列法:

    基本思想是,当产生哈希冲突时,即关键字key的地址p=hash(key)上已经有值了,那么这是以p为基础,产生另外一个哈希地址p1,如果p1不冲突了,那么就将元素key存在位置p1,如果p1也冲突,就计算hash(p1)=p2,不冲突就存在p2,冲突继续计算;

    再散列有几种方式:

       1>线性探测再散列:冲突发生时,查看下个位置是否空,然后遍历下去找到个空的地方存放;

        2>二次探测再散列:冲突发生时,在表的左右进行跳跃探测,di=12 -12 22 -22....k2 -k2;

        3>伪随机探测再散列:di=伪随机序列;

    例子:hash表长度11,哈希函数是:

    h(key) = key%11;那么h(47)=3,h(26)=4,h(60)=5;下一个关键字69,h(69)=3,与47冲突了

    1>线性探测的解决方法:往后遍历找到个空的位置

                0 1 2     3     4    5    6     7     8     9     10

                         47     26   60   69 

    2>二次探测再散列:下个哈希地址是h1 = (3+12)%11 = 4,冲突,再找下一个哈希地址,(3-12)%11 = 2,就放在第二个位置

    3>再看看伪随机数的处理办法,假设随机数是:2 5 9 ,下一个哈希地址(3+2)%11 = 5,冲突,再找下一个,(3+5)%11 = 8,就放在8的位置了。

    2> 再哈希法:

    这种方法是同时构造多个不同的哈希函数:hi = rhi(key) i=1,2...k

    当哈希地址rh1(key)冲突时,再计算hi = rh2(key)....直到不再冲突

    3>拉链法

    hashmap处理冲突的解决方法:将所有冲突的地址放在一个链表中,通过链表链接起来;

    例如:  已知一组关键字(32,40,36,53,16,46,71,27,42,24,49,64),哈希表长度为13,哈希函数为:H(key)= key % 13,则存放的位置如图所示

          位置    Entry 
            0
            1  -->  40 --> 27 --> 53
            2
            3  -->  16 --> 42
            4
            5
            6  -->  32 --> 71
            7
            8
            9
            10 -->  36 --> 49
            11 -->  24
            12 -->  64

    4> 建立公共溢出区: 
    这种方法的基本思想是:将哈希表分为基本表和溢出表两部分,凡是和基本表发生冲突的元素,一律填入溢出表

    优缺点

    开放散列(open hashing)/ 拉链法(针对桶链结构)

    1)优点: ①对于记录总数频繁可变的情况,处理的比较好(也就是避免了动态调整的开销) ②由于记录存储在结点中,而结点是动态分配,不会造成内存的浪费,所以尤其适合那种记录本身尺寸(size)很大的情况,因为此时指针的开销可以忽略不计了 ③删除记录时,比较方便,直接通过指针操作即可
     
    2)缺点: ①存储的记录是随机分布在内存中的,这样在查询记录时,相比结构紧凑的数据类型(比如数组),哈希表的跳转访问会带来额外的时间开销 ②如果所有的 key-value 对是可以提前预知,并之后不会发生变化时(即不允许插入和删除),可以人为创建一个不会产生冲突的完美哈希函数(perfect hash function),此时封闭散列的性能将远高于开放散列 ③由于使用指针,记录不容易进行序列化(serialize)操作

    封闭散列(closed hashing)/ 开放定址法

    1)优点: ①记录更容易进行序列化(serialize)操作 ②如果记录总数可以预知,可以创建完美哈希函数,此时处理数据的效率是非常高的
     
    2)缺点: ①存储记录的数目不能超过桶数组的长度,如果超过就需要扩容,而扩容会导致某次操作的时间成本飙升,这在实时或者交互式应用中可能会是一个严重的缺陷 ②使用探测序列,有可能其计算的时间成本过高,导致哈希表的处理性能降低 ③由于记录是存放在桶数组中的,而桶数组必然存在空槽,所以当记录本身尺寸(size)很大并且记录总数规模很大时,空槽占用的空间会导致明显的内存浪费 ④删除记录时,比较麻烦。比如需要删除记录a,记录b是在a之后插入桶数组的,但是和记录a有冲突,是通过探测序列再次跳转找到的地址,所以如果直接删除a,a的位置变为空槽,而空槽是查询记录失败的终止条件,这样会导致记录b在a的位置重新插入数据前不可见,所以不能直接删除a,而是设置删除标记。这就需要额外的空间和操作。
  • 相关阅读:
    tomcat控制台运行窗口中文乱码
    jquery html5 file 上传图片显示图片
    修改输入框placeholder文字默认颜色-webkit-input-placeholder
    H5移动端知识点总结
    PostgreSQL
    PostgreSQL
    Hexo系列(2)
    SQL
    Java笔记-序列化的注意点
    js、css外部文件的相对路径问题
  • 原文地址:https://www.cnblogs.com/leijiangtao/p/4472095.html
Copyright © 2011-2022 走看看