精华内容
下载资源
问答
  • 参考书籍:《大话数据结构》 1. 开放定址法:一旦发生冲突,就去寻找下一个空散列地址。 只要列表足够大,空散列地址总能找到,并将记录存入。  线性探测法:当使用线性... 随机探测法:在冲突时,对于...

    参考书籍:《大话数据结构》

    1. 开放定址法:一旦发生冲突,就去寻找下一个空的散列地址。

    只要列表足够大,空的散列地址总能找到,并将记录存入。

            线性探测法:当使用线性探测法进时,会吹按不是同义词却需要争夺一个地址的情况,这种现象是堆积。

            二次探测法:增加平方运算为了不让关键字都聚集在某一块区域,解决堆积问题。

            随机探测法:在冲突时,对于位移量di采用随机函数计算得到。注意这里的随机其实是伪随机,根据随机种子得到

    2. 再散列函数法:在冲突时,换用一个散列函数计算。

    这种方法使得关键字不产生聚集,但同时增加了计算时间。

    3. 链地址法:将所有关键字为同义词的记录存储在一个单链表中,在三列表中只存储所有同义词子表的头指针。

    链地址法对于可能会造成很多冲突的散列函数来说,提供了绝不会出现找不到地址的保障,但也带来了查找时需要遍历单链表的性能损耗。

    4. 公共溢出区法:为所有冲突的关键字建立一个公共的溢出区来存放。

     

    在查找时,对给定值通过散列函数计算出散列地址后,咸鱼基本表的相应位置进行对比,如果想等则查找成功;如果不能,则到溢出表进行顺序查找。

    #################################################

    哈希函数的构造方法

    1. 直接定址法:取关键字的某个线性函数作为散列地址

    需要事先知道关键字的分布情况,适合查找表小且连续的情况

    2. 数字分析法

    3. 平方取中法

    4. 除留余数法:

    最常用的哈希函数构造方法,不仅可以直接mod,也可以平方取中、折叠后在mod

    5. 随机数法

    展开全文
  • 3. 解决哈希冲突的方法:数组扩容,设计优秀的哈希函数,开放寻址法和链表法为哈希值找到合适的映射。 4. 开放寻址法,插入、查找、删除都需要相同的寻址逻辑,所以时间复杂度一样。数组中元素越多,空闲位置越少,...

    一、什么是哈希表

    哈希表就是数组+哈希函数,其核心思想是利用数组可以按照下标索引随机访问数据的特性。

    举个栗子:一个班级有50个人,每个人都有学号,按自然数顺序编号,学号1是小明,学号2是小红,学号3是小强,以此类推。在这个栗子中一个班级就是一个数组容器,学号就是数组的下标,学生就是数组中的元素,通过学号找人就是利用数组下标随机访问元素的特性。而如何给学生编号就是哈希函数的事情了。

    在这里插入图片描述

    二、哈希函数

    哈希函数,顾名思义,是一个函数,表达式:hash(key),key就是与数组下标不相干的关键词,而哈希函数计算的值就是与数组下标建立联系,可以直接作为数组下标,也可以将哈希值做取模等运算得到数组下标。

    刚才学号的栗子就是hash(自然数)=自然数,计算得到的哈希值可以直接用作数组下标,而深入到哈希表的实际应用,往往计算出的哈希值会很大,将其直接作为数组下标的话会使数组的长度很长,浪费内存。所以在有限的数组中通过哈希函数映射下标,势必会造成哈希冲突。

    三、哈希冲突的原因与解决方法

    产生哈希冲突的原因不仅仅是因为数组的有界,还包括哈希函数的计算以及对哈希值的映射都会产生哈希冲突。

    解决哈希冲突的方式也是根据这三个原因对症下药:

    1. 数组有界就适当扩容。
    2. 设计优秀的哈希算法。
    3. 开放寻址法和链表法为哈希值找到合适的映射。

    1、数组扩容

    数组中空闲的位置越多,一定程度上哈希冲突也会越小。但是不能因为这个原因就把数组的长度设置的很长,而是设置一个合理的初始长度,后面再慢慢扩容。

    什么时候扩容?扩容多少?都是有考究的。扩容太少导致频繁扩容影响性能,扩容太多浪费内存。一般经验所得,当元素个数占数组长度的3/4时扩容,扩容后的长度是原来的两倍。这也是java中HashMap的扩容思想,但是还是需要根据实际情况做调整。

    什么时候扩容有一个名词,装载因子,代表元素个数占数组长度的比例:装载因子=元素个数/数组长度。装载因子的设置要权衡时间和空间复杂度,装载因子太大,哈希冲突越严重,装载因子太小,内存浪费严重。

    如果内存空间不紧张,对执行效率要求很高,可以降低装载因子的阈值;相反,如果内存空间紧张,对执行效率要求又不高,可以增加装载因子的大小,甚至可以大于 1(对于链表法冲突解决)。

    对于单纯的数组扩容,数据的迁移很简单,对应位置复制过去即可,但是哈希表的扩容迁移就比较复杂,哈希表的长度变了,元素的位置也变了,需要一个个重新计算哈希映射新位置。扩容的时间复杂度是O(n),简单的插入一个数据的时间复杂度是O(1),如果刚好碰上扩容,时间复杂度就是O(n)。
    在这里插入图片描述

    扩容在一定程度上影响插入数据的性能,所以要避免无效的扩容,除了设计合理的装载因子和扩容比例,还可以从扩容的过程中优化:

    (1)扩容动作摊分到每个插入操作中,新数据插入新数组中,插入数据的同时复制一个旧数组中的一个元素到新数组,这样每次插入操作的时间复杂度都是O(1),但是需要兼容维护新旧数组,查找和删除操作先到旧数组查找,没有再到新数组查找。(jdk1.8 ConcurrentHashMap多线程扩容思想)

    (2)对于链表法解决冲突构成的哈希表,迁移时可以链表为单位复制,无需所有元素重新计算哈希值。(ConcurrentHashMap扩容以链表为单位整体迁移复制)

    2、一个优秀的哈希函数

    一个不合理的哈希函数,会使得数组扩容功亏一篑。若计算的哈希值本身很容易冲突,或者映射到数组下标不均匀分布,再多的空闲位置也没用。这就要求一个优秀的哈希函数必须具有以下2个要素:

    • 哈希值尽量随机且均匀分布,这样不仅可以最小化哈希冲突,而且即使出现了冲突,也会平均在各个位置,有利于冲突的解决改善(开放寻址法和链表法)。
    • 哈希算法的计算性能要高,不能影响哈希表的正常操作。

    数组扩容和优秀的哈希函数仍然无法避免哈希冲突,还可以从哈希值映射上下手,常用的方法有开放寻址法和链表法。

    3、开放寻址法

    开放寻址法,就是当发生哈希冲突时,重新找到空闲的位置,然后插入元素。寻址方式有多种,常用的有线性寻址、二次方寻址、双重哈希寻址:

    • 线性寻址,当需要插入元素的位置被占用时,顺序向后寻址,如果到数组最后也没找到一个空闲位置,则从数组开头寻址,直到找到一个空闲位置插入数据。线性寻址的每次寻址步长是1,寻址公式hash(key)+n(n是寻址的次数)。
    • 二次方寻址,就是线性寻址的总步长的二次方,即hash(key)+n^2
    • 双重哈希寻址,顾名思义就是多次哈希直到找到一个不冲突的哈希值。
      在这里插入图片描述

    采用开放寻址法解决哈希冲突,又该如何查找元素和删除元素呢?

    查找元素的过程和插入元素类似,用相同的寻址方式,寻址的同时比对key或者value是否相等,相等则认为元素存在,不相等则继续寻址,如果探测到空闲位置依然没有找到则认为该元素不存在

    删除有些特别,不能单纯的把要删除的元素设置为空,因为在查找元素的过程中探测到的空闲位置是删除元素的位置,就会使得查找元素的寻址算法失效,本来存在的元素误判定为不存在。该如何解决这个问题呢?

    只需要删除元素不是物理删除而是逻辑删除。给删除的元素做上delete标记,当查询元素寻址时遇到delete标记的位置时不会停下来而是继续向后探测,但是在插入元素寻址遇到delete标记的位置就会把应该删除的元素替换掉。

    三种寻址方式都有着明显的不足:

    • 线性寻址,寻址的性能虽然元素个数的增多逐步下降,最坏时间复杂度是O(n)。
    • 二次方寻址,寻址的次数比线性寻址较低了,但是会因为步长是二次方,所以需要较长的数组长度,内存利用率可能较低。
    • 双重哈希寻址,多次哈希可能会浪费时间,需要优质的哈希函数做支撑。

    而整个开放寻址法的不足也很明显:

    • 插入、查找、删除都需要寻址。
    • 数组中元素越多,空闲位置越少,哈希冲突越剧烈。所以装载因子不能太大,要及时扩容减小冲突,但是数组内存利用率较低。

    看似开放寻址法有挺多问题,但是也有一些优点:

    • 数据都存储在数组中,可以有效地利用 CPU 缓存加快查询速度。
    • 而且,这种方法实现的哈希表,序列化也简单,不像链表还要考虑指针。

    总结而得,当数据量比较小、装载因子小的时候,适合采用开放寻址法。这也是 Java 中ThreadLocal内部类ThreadLocalMap使用开放寻址法解决散列冲突的原因。

    4、链表法

    链表法相对于开放寻址法实现起来简单一些,在数组内存利用率上比开放寻址法高,同时对装载因子的忍耐度也相对较高。开放寻址法的装载因子只能小于1,越趋近于1,冲突越剧烈,寻址过程越耗时,而链表法的装载因子可以大于1(但是内存不紧张,在意性能的一般不会装载因子不会大于1)。

    链表法就是将产生哈希冲突的元素链接成一个链表,每个链表可以设想成一个桶(bucket)或者槽(slot):

    • 插入元素就是通过哈希找到对应的桶,然后插入到链表中,时间复杂度为O(1);
    • 查找元素也是通过哈希找到对应的桶,然后遍历链表;
    • 删除元素同样通过哈希找到对应的桶,遍历链表找到需要删除的元素删除。

    当哈希比较均匀时,理论上查询和删除的时间复杂度为O(n/m),n是数组中元素的个数,m是数组中桶的个数。但是当哈希冲突非常严重时,数据都集中在一个桶里,数组退化成链表,查找和删除的时间复杂度为趋近与O(n)。
    在这里插入图片描述

    针对数组退化成链表或者链表过长导致的性能下降,可以在合适的时机将链表转换为红黑树,极端情况下数组退化成一个红黑树,时间复杂度也是O(logn),比O(n)强多了。(jdk8中ConcurrentHashMap对于jdk7有所优化,当链表节点的个数大于8个且数组的长度大于64时,链表转换为红黑树;当红黑树的节点小于8个时又退化为链表。)

    可以容忍的缺点:

    • 因为链表节点需要存放指针,所以内存占用上比开放寻址法高。
    • 链表中的节点在内存中是不连续分布的,所以对CPU缓存的利用率也不高,序列化也比开放寻址法复杂。

    优点:

    • 内存利用率较高。
    • 优化策略灵活,红黑树和链表可以互相转换。

    四、总结

    哈希表的两个核心哈希函数的设计与哈希冲突的解决。

    1. 哈希表就是数组+哈希函数,其核心思想是利用数组可以按照下标索引随机访问数据的特性。
    2. 哈希冲突的原因:数组的有界,哈希函数的计算,哈希值的映射。
    3. 解决哈希冲突的方法:数组扩容,设计优秀的哈希函数,开放寻址法和链表法为哈希值找到合适的映射。
    4. 开放寻址法,插入、查找、删除都需要相同的寻址逻辑,所以时间复杂度一样。数组中元素越多,空闲位置越少,哈希冲突越剧烈。
    5. 链表法需要注意,当哈希冲突非常严重时,数组会退化成链表,查找和删除的时间复杂度趋近于O(n),可以采用红黑树进行优化。

    参考:极客时间专栏《数据结构与算法之美》。

    PS: 如若文章中有错误理解,欢迎批评指正,同时非常期待你的评论、点赞和收藏。我是徐同学,愿与你共同进步!

    展开全文
  • 解决哈希冲突的四种方法导语(一)开放定址法(1)线性探查法(2)线性补偿探查法(3)随机探测(4)二次探查(二)再哈希法(三)链地址法(四)建立公共溢出区结束语 导语 哈希表是我们在开发中经常使用到的一种...

    导语

    哈希表是我们在开发中经常使用到的一种数据结构。它的结构也决定了它不可避免地会产生冲突。那么,如何解决哈希冲突呢?

    我搜索了一些资料,如今结合自己的理解做一个学习记录。

    我们先给出一个产生哈希冲突的例子:

    (16、25、3、29、5、2、19、17、34)
    Hash(key)= key % 11。

    对应的地址应该是:

    5、3、3、7、5、2、8、6、1

    很显然存在哈希冲突。
    接下来我们来看一下不同方法分别是如何处理这种冲突的:

    (一)开放定址法

    第一种方法是开放定址法,也就是说当冲突发生时,使用某种探查技术生成一个探查序列,再按照该序列逐个寻找。

    插入:

    • 寻找一个开放的地址,将待插入节点存入该地址单元,插入成功
    • 如果查找到了最后一个地址仍未找到,则代表该表已满,插入失败。

    查找:

    • 寻找给定的关键字,查找成功;
    • 如果找到了开放的地址,或者查找到了最后一个地址,说明表中无待查关键字,查找失败。

    有以下几种常见的探查技术:

    (1)线性探查法

    h(key) = d;
    探查序列为:d、d+1、d+2、…、0、1、…、d-1。

    解决方案

    (16、25、3、29、5、2、19、17、34)
    Hash(key)= key % 11。

    哈希值: 533 475 6286 7 8 9、1

    0 1 2 3 4 5 6 7 8 9 10
    34 2 25 3 16 5 29 19 17

    缺点:

    • 容易造成堆聚现象。所谓堆聚现象,就是存入哈希表的记录在表中连成一片。这样一来,再有新的记录时更容易发生冲突,也就是说非同义词也可能发生冲突,进一步造成堆聚。
    • 删除工作困难,只能标记该节点已删除,而不能真正置空该位置因为会截断其后的同义词的查找这是所有开放定址法的共同缺陷。
    • 如果发生溢出,要另外设立顺序溢出表。

    (2)线性补偿探查法

    h(key)=d;
    哈希表长度为n,q与n互质(能保证遍历所有位置)。
    探查序列为:di = (d+q)%n(0<=i<=n-1)

    解决方案

    (16、25、3、29、5、2、19、17、34)
    Hash(key)= key % 11。
    不妨取q = 5。

    哈希值: 533 875 1028 2 7 161 6 0

    0 1 2 3 4 5 6 7 8 9 10
    34 19 2 25 16 17 29 3 5

    缺点:

    • 同线性探测,删除困难。

    (3)随机探测

    随机探测就是将线性探测的步长从常数改为随机数。

    不同的关键字随机产生不同的探测序列,可以减少堆聚。

    优点:

    • 减少堆聚

    缺点:

    • 尽管不同关键字具有不同的探测序列,但同一关键字的探测序列却是固定的,所以仍然不能直接删除,而只能打上删除标记。

    (4)二次探查

    探查序列为:di = h(key) + i2(i>=0).

    不能遍历所有空位,但当用该办法查找不到空位时,也是该扩充哈希表的时候。

    (二)再哈希法

    再哈希法也叫再散列法,是指当发生冲突时,对得到的哈希值进行再次哈希,直至不再发生冲突为止。需要多个不同的哈希函数。
    h1(key) = d1;
    h2(key) = d2;

    hi(key) = di;

    解决方案

    (16、25、3、29、5、2、19、17、34)
    Hash1(key)= key % 11;
    Hash2(key)= 3key % 7;
    Hash3(key)= key2 % 3;

    哈希值: 533 275 12 10861 4

    0 1 2 3 4 5 6 7 8 9 10
    5 3 25 34 16 17 29 19 2

    缺点:

    • 每次冲突都要重新散列,计算时间增加。

    (三)链地址法

    链地址法也叫拉链法,即 将具有相同哈希值的结点链接到对应地址上的单链表上。这样,整个哈希表实际是一个链表数组。

    解决方案

    (16、25、3、29、5、2、19、17、34)
    Hash(key)= key % 11。
    不妨取q = 5。

    哈希值:
    533752861

    0 1 2 3 4 5 6 7 8 9 10
    34 2 25 16 17 29 19
    3 5

    优点:

    • 处理冲突简单,且无堆聚现象,平均查找长度较短;
    • 灵活,适合于造表前无法确定表长的情况;
    • 负担大于1的装载因子,且结点较大时,指针域所占空间可以忽略不计;
    • 删除结点的操作易于实现

    缺点:

    • 当结点空间较小时,指针域所占空间不可忽略。

    (四)建立公共溢出区

    解决方案

    (16、25、3、29、5、2、19、17、34)
    Hash(key)= key % 11;

    哈希值: 533 075 12861

    哈希表 0 1 2 3 4 5 6 7 8 9 10
    34 2 25 16 17 29 19
    公共表 0 1 2 3 4 5 6 7 8 9 10
    3 5

    结束语

    用例子推演了一遍,才发现自己之前对再哈希法的理解有误。

    之前误以为是将哈希之后得到的值再哈希,遇到了哈希前哈希后值相同的问题,造成逻辑上的死循环。

    推演时发现了该问题,实则是使用其他的哈希函数原关键字进行再哈希。

    展开全文
  •  其中H(key)为哈希函数,m 为长,di称为增量序列。根据增量序列取值方式不同,具体到下面三种散列方法:  线性探测再散列:di=1,2,3,…,m-1  二次探测再散列:di=1^2,-1^2,2^2,-2^2,…,k^2,-k^...

    1、开放定址法:

      Hi=(H(key)+di)% m

      其中H(key)为哈希函数,m 为表长,di称为增量序列。根据增量序列的取值方式不同,具体到下面三种散列方法:

      线性探测再散列:di=1,2,3,…,m-1

      二次探测再散列:di=1^2,-1^2,2^2,-2^2,…,k^2,-k^2 ( k<=m/2 )

      伪随机探测再散列:di=伪随机数序列

     

    2、再哈希法:

      同时构造多个不同的哈希函数,当第一个哈希地址发生冲突的时候,用第二个哈希函数来计算……,直到冲突不再发生。

     

    3、链地址法:

      相当于HashMap中还没到阀值前的链表散列。

     

    4、建立一个公共溢出区:

      将哈希表分为基本表和溢出表两部分,凡是和基本表发生冲突的元素,一律放入溢出表。

    转载于:https://www.cnblogs.com/LinsenLi/p/9674234.html

    展开全文
  • 哈希存储和冲突解决办法

    千次阅读 2013-11-10 21:59:58
    一、哈希表的概念及作用  一般线性表,树中,记录在结构中相对位置是随机的,即和记录关键字之间不存在确定关系,因此,在 结构中查找记录时需进行一系列和关键字比较。这一类查找方法建立在“比较...
  • 哈希表

    2017-03-21 16:47:00
    哈希表解决冲突的方法: 1 开放定址法(再散列法):  线性再散列 H(k)+di % mod ; di = [1, n]  二次再散列 H(k) + di % mod di = [-1^2, 1^2, 2^2, -2^2, 。。。] 跳跃选址  随机再散列 H(k)+di % mod ;...
  • 哈希表的特点:关键字在表中位置和它之间不存在一个确定关系,查找过程为给定值一次和各个关键字进行比较,查找效率取决于和给定值进行比较次数。  哈希表的特点:关键字在表中位置和它之间存在一种...
  • 初识哈希表

    2019-10-08 22:47:33
    解决散列冲突的方法哈希表的性能: 插播阿里面试题: 是什么: 散列表又名哈希表、hash表 Java中对应的数据结构有hashmap Python中对应的数据结构叫做 字典 它是一种基于数组,借助散列函数进行扩展的数据...
  • 开放定址法 线性探测再散列 二次探测再散列 ...通过构造性能良好哈希函数,可以减少冲突,但一般不可能完全避免冲突,因此解决冲突是哈希法另一个关键问题。创建哈希表和查找哈希表都会遇到冲突,...
  • 哈希表应用

    2018-08-31 15:02:42
    理论上说,当输入规模够大时是不可能没有冲突的,因为两个域要有映射,且结果域是远远小于输入域的,选取一个好的哈希函数固然重要,但是解决冲突的方法也是必不可少。 这里先普及一些东西:哈希函数,在随机大样本...
  • 二次探测再散列:冲突发生时,在表的左右进行跳跃探测,di=12 -12 22 -22....k2 -k2; 3>伪随机探测再散列:di=伪随机序列; 再哈希法 这种方法是同时构造多个不同的哈希函数: Hi=RH1(key) i=1,2,…,k 当...
  • C语言设计哈希表实现图书查找系统,完成相应建表和查表程序。...3) 分别采用线性法、随机法、溢出法解决冲突,比较不同方法的冲突率,计算不同方法的平均查找长度。 4) 查找并显示给定图书编码记录。
  • 哈希表(初识)

    2020-06-16 17:15:25
    哈希表 1、哈希表也叫散列表,依赖了数组下标的随机访问能力,实现了更...4、解决哈希冲突的方法:闭散列,开散列 闭散列 1.核心思路:在冲突位置开始往后找到一个合适的位置来存放这个冲突的值,当前时采用闭散列中
  • 解决hash冲突的三个方法

    千次阅读 2018-03-19 09:23:01
    目录开放定址法线性探测再散列二次探测再散列伪随机探测再散列再哈希法链地址法建立公共溢出区优缺点开放散列(open ...创建哈希表和查找哈希表都会遇到冲突,两种情况下解决冲突的方法应该一致。下面以创建哈希...
  • hash冲突解决方法

    2018-06-24 11:06:34
     以发生冲突的哈希地址为自变量,通过线性探查法、平方探查法、伪随机序列法、双哈希函数法等处理得到一个新的空闲的哈希地址 2、拉链法(hashmap处理冲突解决方法)  拉链法 的实现比较简单,将链表和数组相...
  • 2:使用哈希表存储以上数据(在存储数据时要考虑哈希函数设计,冲突解决方法等问题) 3:使用普通链表存储以上数据 4:分别使用相同数据测试查找速度10000,100000,1000000,…并记录时间,比较两者有何不同。...
  • 哈希

    2016-09-23 17:21:55
    哈希哈希冲突解决办法链式哈希表将数据存储在桶中-桶是链表。如果冲突,增大链表长度 开发地址哈希表数据本身存储在链表中,通过探测避免冲突 哈希方法的目的将键均匀,随机分布到表中哈希函数1 取余法,选择因子 ...
  • 哈希表获取要考虑全部可能空间。 在链地址法中,可能空间就是具有相同hash值链表。 目录 开放定址法 线性探测再散列 二次探测再散列 伪随机探测再散列 再哈希法 链地址法 建立公共溢出区 优...
  • java 解决Hash(散列)冲突的四种方法:1.开放定址法(线性探测,二次探测,伪随机探测)2.链地址法3.再哈希4.建立公共溢出区本文以图示+文字说明的方式,解释什么是开放地址中的线性探测和链地址法1.线性探测如图所示:...
  • 问题分析  本题考察的是客户订单数据库中主键的存放,我们需要构想一种索引方法以实现用户订单信息的快速存取。 解决思路  可以定义一个 10x10 二维...至于处理冲突的方法可以采用拉链法,即如果又有电话为138*...
  • 哈希查找是通过计算数据元素存储地址进行查找一种方法。O(1)查找,即所谓秒杀。哈希查找本质是先将数据映射成它哈希值。...2) 根据选择的冲突处理方法解决地址冲突; 3) 在哈希表的基础上执行哈希查...
  • 哈希查找

    2017-03-13 22:50:50
    哈希查找是通过计算数据元素存储地址进行查找一种方法。O(1)查找,即所谓秒杀。哈希查找本质是先将...根据选择的冲突处理方法解决地址冲突; 在哈希表的基础上执行哈希查找。 public class hash { public
  • 哈希与一致性哈希

    2019-09-06 22:41:53
    1 哈希解决冲突的三种方法 开放定址法 也称再散列法,其基本思想是:当关键字key的哈希地址p=H(key)出现冲突时,以p为基础,产生另一个哈希地址p1,如果p1仍然冲突,再以p为基础,产生另一个哈希地址p2,…,直到...
  • 解决方法:1)开放寻址法:当散列冲突发生时,重新探测一个空闲位置,将其插入。一般是在数据量比较小,装载因子小时采用。用装载因子表示空位多少:散列表装载因子 = 填入元素个数 / 散列表长度 ...

空空如也

空空如也

1 2 3
收藏数 54
精华内容 21
关键字:

哈希表解决冲突的方法随机