Java集合(九)哈希冲突及解决哈希冲突的4种方式

一、哈希冲突

(一)、产生的原因

哈希是通过对数据进行再压缩,提高效率的一种解决方法。但由于通过哈希函数产生的哈希值是有限的,而数据可能比较多,导致经过哈希函数处理后仍然有不同的数据对应相同的哈希值。这时候就产生了哈希冲突。

(二)、因素

  • 装填因子(装填因子=数据总数 / 哈希表长);
  • 哈希函数;
  • 处理冲突的方法。

(三)、解决哈希冲突的4中方式

开放地址法;再哈希法;链地址法(拉链法);公共溢出区法。

二、开放地址法

开放地址法处理冲突的基本原则就是出现冲突后按照一定算法查找一个空位置存放。公式:

其中:Hi为计算出的地址,h(key)为哈希方法,di增量序列1,2,3,...,k(k<= m - 1),m为哈希表的长度。

假设问题:关键码集合为:{38,25,74,63,52,48,55},m = 7,采用除留余数法h(key) = key mod 7,并存储在哈希表中。

(一)、线性探测:依次向后查找

从上图可以看出,38和52存放3号地址冲突,25和74存放4地址冲突,根据集合,可以知道,38先存放在了3,25先存放了4,所以将74和52进行线上探测,根据公式,线上探测74时,取d = 1,探测52时,取d = 5,最终结果如下表:

优点:只要哈希表未被填满,保证能找到一个空地址单元存放有冲突的元素。

缺点:能使第i个哈希地址的同义词存入第i+1个地址,这样本应存入第i+1个哈希地址的元素变成了第i+2个哈希地址的同义词,产生“聚集”现象,降低查找效率。

(二)、二次探测:依次向前后查找,增量为1、2、3的二次方

以上面(一)线上探测74为例,根据公式,取d = 1²,最终结果如下表:

(三)、伪随机探测:随机产生一个增量位移

还是以74为例,根据公式,取d = 29时,最终结果如下表:

(四)、建立哈希表的步骤

1、取数据元素的关键码key,计算其哈希函数值(地址)。若该地址对应的存储 空间还没有被占用,则将该元素存入;否则执行2解决冲突。

2、根据选择的冲突处理方法,计算关键码key的下一个存储地址。若下一个存储地址仍被占用,则继续执行2,直到找到能用的存储地址为止。

三、再哈希法

再哈希法,又叫双哈希法,有多个不同的Hash函数,出现冲突后采用其他的哈希函数计算,直到不再冲突为止。虽然不易发生聚集,但是增加了计算时间。公式:

其中RHi为不同的哈希函数。比如乘余取整法:RH(k)=[b ×(a × k mod 1)] ,还是以上面74为例:设b = 10,a = 0.6180339,根据公式有:RH(74)=[10 ×(0.6180339 × 74 mod 1)]  = 7,最终结果如下表:

四、拉链法(链地址法)

将具有相同哈希地址的记录链成一个单链表,m个哈希地址就设m个单链表,,然后用一个数组将m个单链表的表头指针存储起来,形成一个动态的结构。

优点:

1、拉链法处理冲突简单,且无堆积现象,即非同义词决不会发生冲突,因此平均查找长度较短;

  2、由于拉链法中各链表上的结点空间是动态申请的,故它更适合于造表前无法确定表长的情况;

  3、开放定址法为减少冲突,要求装填因子α较小,故当结点规模较大时会浪费很多空间。而拉链法中可取α≥1,且结点较大时,拉链法中增加的指针域可忽略不计,因此节省空间;

  4、在用拉链法构造的散列表中,删除结点的操作易于实现。只要简单地删去链表上相应的结点即可。

缺点:

1、指针占用较大空间时,会造成空间浪费,若空间用于增大散列表规模进而提高开放地址法的效率。

假设:关键字集合{47,7,29,11,16,92,22,8,3,50,37,89},m = 11,哈希算法为H(k) = k mod 11,则建表如下图:

(一)、建立哈希表的步骤

1、取数据元素的关键码key,计算其哈希函数值(地址)。若该地址对应的链表为空,则将该元素插入此链表;否则执行2解决冲突。

2、根据选择的冲突处理方法,计算关键码key的下一个存储地址。若该地址对应的链表不为空,则利用链表的前插法或后插法将该元素插入此链表。

(二)、特点

1、非同义词不会冲突,无“聚集”现象;

2、链表上的结点空间动态申请,适用于表长不确定的情况。

五、公共溢出区法

创建哈希表时,将所有产生冲突的的同义词集中放在一个溢出表中。假设哈希函数的值域是[1,m-1],则设哈希表HashTable[0...m-1]为基本表,每个分量存放一个记录,另外设溢出表OverTable[0,v]为溢出表,所有关键字和基本表中关键字为同义词的记录,不管它们由哈希函数得到的哈希地址是什么,一旦发生冲突,都填入溢出表。

例子:关键码集合{26,36,41,38,44,15,68,12,6,51,25},m = 12,哈希函数:H(k)= k mod 12,则哈希表如下:

上图蓝色部分,元素的哈希地址冲突了,此时创建一个溢出表:

Java集合(九)哈希冲突及解决哈希冲突的4种方式的更多相关文章

  1. Java的IO操作中有面向字节(Byte)和面向字符(Character)两种方式

    解析:Java的IO操作中有面向字节(Byte)和面向字符(Character)两种方式.面向字节的操作为以8位为单位对二进制的数据进行操作,对数据不进行转换,这些类都是InputStream和Out ...

  2. Java命令行启动jar包更改默认端口以及配置文件的几种方式

    Java命令行启动jar包更改默认端口以及配置文件的几种方式 java -jar xxx.jar --server.port=8081 默认如果jar包没有启动文件,可以采用这种方式进行启动 java ...

  3. Java多线程系列--“基础篇”02之 常用的实现多线程的两种方式

    概要 本章,我们学习“常用的实现多线程的2种方式”:Thread 和 Runnable.之所以说是常用的,是因为通过还可以通过java.util.concurrent包中的线程池来实现多线程.关于线程 ...

  4. 将Java web应用部署到Tomcat 及部署到Tomcat根目录 的三种方式

    Tomcat作为Servlet/JSP容器(服务器)挺不错的,开源免费,需要知道的是Tomcat是一个Web服务器,其符合Servlet/JSP规范,但是却没有实现所有JavaEE规范,所以我们还是应 ...

  5. java客户端验证https连接(忽略证书验证和证书验证两种方式)

    首先根据如下操作生成证书,配置springboot https,生成一个简单的https web服务 https://www.cnblogs.com/qq931399960/p/11889349.ht ...

  6. Java多线程并发01——线程的创建与终止,你会几种方式

    本文开始将开始介绍 Java 多线程与并发相关的知识,多谢各位一直以来的关注与支持.关注我的公众号「Java面典」了解更多 Java 相关知识点. 线程的创建方式 在 Java 中,用户常用的主动创建 ...

  7. Java常见重构技巧 - 去除不必要的!=null判断空的5种方式,很少有人知道后两种

    常见重构技巧 - 去除不必要的!= 项目中会存在大量判空代码,多么丑陋繁冗!如何避免这种情况?我们是否滥用了判空呢?@pdai 常见重构技巧 - 去除不必要的!= 场景一:null无意义之常规判断空 ...

  8. java web中日期Date类型在页面中格式化显示的三种方式

    一般我们经常需要在将服务器端的Date类型,传到页面进行显示,这就涉及到一个如何格式化显示Date类型的问题,一般我们有三种方式进行: 1)在服务端使用SimpleDateFormat等类格式化成字符 ...

  9. Java集合(八)哈希表及哈希函数的实现方式

    Java集合(八)哈希表及哈希函数的实现方式 一.哈希表 非哈希表的特点:关键字在表中的位置和它之间不存在一个确定的关系,查找的过程为给定值一次和各个关键字进行比较,查找的效率取决于和给定值进行比较的 ...

随机推荐

  1. qt creator源码全方面分析(4-2)

    目录 global头文件 global.h xx.h global头文件 插件的本质就是动态链接库,对于库,需要导出符号,供用户导入使用.在qt creator的源码中,存在固定的导入导出模式. gl ...

  2. Mysql数据库多表联查

    内连接:查询的是多个表的交集 外连接:查询的是一张表的全部数据和另一张表满足要求的数据 student数据库表 grade数据库表 内连接: 隐式内连接 SELECT s.id, s. NAME, s ...

  3. Java常见的集合的数据结构

    数据结构 数据结构__栈:先进后出 栈:stack,又称堆栈,它是运算受限的线性表,其限制是仅允许在标的一端进行插入和删除操作,不允许在其他任何位置进行添加.查找.删除等操作. 简单的说:采用该结构的 ...

  4. Mybatis使用ResultMap

    解决字段名和属性名不一致的问题 - 新建数据库表的字段-这里就不贴上了 在下面链接有 https://www.cnblogs.com/rzkwz/p/12853899.html 设置实体类和数据库字段 ...

  5. QtCreator中使用链接库

    说明 之前讨论的DLL的静态链接和动态连接都是基于 MSVC 编译器,但是 MinGW 似乎有另外一套类似但是不相同的机制.下文均在 windows 下使用 Qt Creator 中使用 MinGW ...

  6. STM32 OSAL操作系统抽象层的移植

    文章目录 什么是 OSAL? 源码安装 Linux 上OSAL的移植 STM32上OSAL的移植 关键点 测试代码 结语 附件 什么是 OSAL? 今天同学忽然问我有没有搞过OSAL,忽然间一头雾水, ...

  7. Python拆分一列为多列

    有的员工,没有公司开户行的银行卡,发放现金工资.有时人多,需要计算币数.现金工资表中,其中一列为实发工资,import pandas as pd,转化为pd.DataFrame. 面值[100,50, ...

  8. 使用JDBC操作MySQL

    使用JDBC操作MySQL 步骤 加载驱动 连接数据库 操作数据库(增删改查) 关闭结果集,操作,数据库 准备工作 java连接MySQL的jar包 加载数据库驱动 public class Load ...

  9. Linux常用命令详解—基于CentOS7

    ## Linux 目录- /:根目录,一般只存放目录,不存放文件- /bin -> /usr/bin:可执行二进制文件的目录,也是常用命令目录,如常用的命令 ls.cat.mv 等- /boot ...

  10. Linux中的vi编辑器使用

    总是忘记,我就谢谢 touch XXX文件名 vi XXX文件名 敲击 i 进入编辑模式 敲击ESC 退出编辑模式 退出编辑模式后 输入:wq!保存并退出 输入:q!不保存退出 查看文件:cat XX ...