哈希第一弹:哈希的概念、哈希函数以及哈希冲突的解决方法

    技术2023-03-25  106

    哈希的概念: 

    哈希表查找的基本思想是:根据当前带查找数据的特征,以记录关键字为自变量,设计一个哈希函数,以该函数按关键码计算该元素的存储位置,并按此存放;查找时,有同一个函数对给定值key计算地址,将key与地址单元中元素关键码进行比较,确定查找是否成功。哈希方法中使用的转换函数称为哈希函数,按这个思想构造的表称为哈希表(散列表)

     

    从上面的概念,构建哈希表关键是有哈希函数。那么我们来看看常见的哈希函数有哪些?

     

    常见哈希函数:

    直接定值法:

    取关键字的某线性函数为哈希地址:Hash(Key) = A*Key +B ;

    eg: 4 6 7 8 9          Hash(Key) =Key

        4 6789

     

                                                          0            1         2      3          4         5          6          7            8              9 

    直接定制法,比较好的一点就是简单,直接映射;  不过也有缺点:映射的数据集合应该是分布集中,连续的 

     eg:1    100000000000

    如果只映射两个数1和一个超大的数,直接映射,我们需要开很大的空间,并且空间严重浪费,这就是直接定制法的巨大缺陷。

     除留余数法:

           假设散列长度为m,取一个不大于m但最接近或者等于m的质数p作为除数       按照哈希函数 Hash(key) =key%p,将关键码转换成哈希地址

     

     除留余数法,很大程度解决了直接定制法的空间浪费问题。

     不常用的哈希函数

     平方取中法

                先计算关键字的平方,再抽取中间的几个数作为哈希地址

      eg :          

    关键字平方哈希地址(抽取了中间3位)12341522765227432118671041710(671也可以)

     用平方取中法,关键字的位数不能太大。太大的话,最早的情况,平方之后如果超过long  long,很难操作

    折叠法

      将关键字从左到右分隔成位数相等的几部分(最后一部分位数可以短一些) 再将这几部分叠加求和,并按散列表表长,取后几位最为散列地址   

     

     折叠法可以适用于关键字位数较大的情况

     随机数法:

     选择一个随机函数,取关键字的随机函数值为哈希地址;即Hash(key)=random(key)通常应用于关键字长度不等时采用这种方法

     数字分析法:

          如果有n个d位数,每一位上可能由r中不同的符号,这种r中不同的符号在各个位上出现的概率不一定相同,可能在某些位上分布比较均匀,每种符号出现的机会均等,在某些位上分布不均匀,只有某几种符号常见。我们就可以根据散列表的大小,选择其中各个符号分布均匀的若干位作为散列地址。通常用于关键字位数表加大,并且实现直到关键字的若干位分布均匀的情况

     总之,设计哈希函数的目的,一是为了映射到哈希表上,二是为了减少产生哈希冲突的可能性(无法完全避免哈希冲突) 

     

    可能刚才提到哈希冲突,大家有点迷惑什么是哈希冲突?

    接下来,我们就讨论一下哈希冲突及其解决方法。我们主要讨论的哈希函数为直接定制法和除余留数法。

    哈希冲突

    哈希的概念简单的说就是将关键字映射到表(哈希表)上的某个位置,查询的时候,到这个位置上去找;

    那哈希冲突呢? 冲突,就是我们常说的矛盾。即多个关键字,经过哈希函数计算,映射到相同的位置,此时就有了矛盾,这个位置该存哪个关键字呢!这也就是我们说的哈希冲突 

     以我们上文用到的除余留数法为例,理解哈希冲突

     上面我们也提到了哈希冲突是不可避免的(当然,直接定值法,进行一 一映射的情况,可以视为特殊),所以我们需要解决方法:

    哈希冲突的解决方法

    闭散列(开放定址法):

     当发生哈希冲突时,如果哈希表未被装满,说明在哈希表中必然还有空位置,那么可以把关键字存放到冲突位置中的“下一个”空位置中去。

                如何寻找下一个空位置呢?

       线性探测:

               从发生冲突的位置开始,依次向后探测,直到寻找下一个空位置为止

      二次探测:

             从发生冲突的位置开始,找下一个空位置,查找空位置的方法为 (+i^2)%m,i=1,2,3........;m是哈希表大小,是经过关键字计算后的出的哈希地址

    开散列(链地址法、开链法)

     对关键字集合用散列函数计算散列地址,具有相同地址的关键码归于同一个子集合,每一个子集和称为一个桶,各个桶中的元素通过一个单链表连接起来,各链表的头结点存储在哈希表中。

    举例理解上面三种解决哈希冲突的方法

    线性探测:

    从4这个位置开始查找下一个空位置,到5这个位置的时候,发现没有关键字存放,则把444放在5这个位置,倘若又要插入4444,则又要从4这个位置查找下一个空位置,发现6这个位置没有关键字存放,则把4444放到6这个位置;

     

    看图解:

     二次探测:

    当插入444时,i=1,经过计算,判断5的位置是否有关键字存放,发现没有,则将444存放在5这个位置

    当在插入4444时,i=2,经过计算,判断8的位置是否有关键字存放,发现没有,则将4444存放在8这个位置

     

     开链法:

    桶结构,将相同关键字存放在一个桶上。

     

     

     

    注:如果本篇博客有任何错误和建议,欢迎伙伴们留言,你快说句话啊!

     

     

    Processed: 0.013, SQL: 9