Java集合(八)哈希表及哈希函数的实现方式
Java集合(八)哈希表及哈希函数的实现方式
一、哈希表
非哈希表的特点:关键字在表中的位置和它之间不存在一个确定的关系,查找的过程为给定值一次和各个关键字进行比较,查找的效率取决于和给定值进行比较的次数。
哈希表的特点:关键字在表中位置和它之间存在一种确定的关系。
哈希函数:一般情况下,需要在关键字与它在表中的存储位置之间建立一个函数关系,以f(key)作为关键字为key的记录在表中的位置,通常称这个函数f(key)为哈希函数。
哈希(hash) : 翻译为“散列”,就是把任意长度的输入,通过hash算法,变成固定长度的输出,该输出就是hash值。
这种转换是一种压缩映射,hash值的空间通常远小于输入的空间,不同的输入可能会散列成相同的输出,所以不可能从hash值来唯一的确定输入值。
简单的说就是一种将任意长度的消息压缩到固定长度的消息摘要的函数。
实现哈希函数一般有6种方式:直接定址法、数字分析法、平方取中法、折叠法、除留余数法、 随机函数法。其中最常用的是除留余数法。
二、直接定址法
取关键码的某个线性函数值作为哈希地址。公式:H(k) = a × k + c (其中a,c为常数)。
优点:以关键码k的某个线性函数值为哈希地址,不会产生冲突。
缺点:会占用连续的地址空间,空间效率低。
当向字典中加入某一新元素时算法自动调用此函数,以确定该元素最终的存储位置。若某元素关键码key为1,上式中,a=2,b=3则该元素最终会存储在字典第5个位置中。
例子:关键码集合为{100,200,500,700,900},选取哈希方法为:H(k) = k / 100,则哈希表(存储结构)如下:

直接定址法的优点是实现方法简单,算法时间复杂度较小,而且不会产生冲突。但是,直接定址法要求散列地址空间的大小与关键码集合的大小一致,而这种要求是苛刻的,一般很难实现。例如当关键码的范围为1~1000000时,元素散列地址的个数也要达到1000000。这么大的散列地址是不合实际的。
特点:一般不会发生冲突,但适用面比较窄。
三、数字分析法
取关键码的中的若干位作为哈希地址。选用的原则应当是:各种符号在该位上出现的频率大致相同。
例子:有一组(例如80个)关键码,如下表:

1、位号为①、②的关键码均是“3和4”,③也是只有“7,8,9”,因此,这几位不应作为哈希地址,余下的四位,分布较均匀,可作为哈希地址选用。
2、若哈希地址取2位数,则可取④、⑤、⑥和⑦中的任意两位组成哈希地址,也可取其中两位和另外两位叠加求和后,取低的两位作为哈希地址。
特点:适用于事先明确知道表中所有关键码每一位数值的分布情况。它完全依赖于关键码集合,如果换一个关键码集合,选择哪几位作为哈喜欢地址要重新确定。
四、平方取中法
对关键码平方后,按哈希表大小,取中间若干位作为哈希地址。
例子:2589的平方值为6702921,可以取中间的029作为哈希地址。
特点:对数字分析法的改进,哈希地址与关键码的每一位都相关。
五、折叠法
将关键码分割成位数相同的几部分(最后一部分可以不同),然后取这几部分的叠加和作为哈希地址。折叠法有两种,即位移法和分界法。
(一)、位移法:采取的具体方式是把各部分的最后一位对齐相加
(二)、分界法:采用的具体方式是各部分不折断,而沿各部分的分界来回折叠,然后对齐相加,并将相加的结果当做散列地址
假设关键码key=987654321,散列地址为4位。位移法和分界法计算散列地址的算式如下图:

由式可见,位移法计算结果为15309,由于散列地址为4位,所以舍去最高位数字1,元素最终的散列地址为5309。分界法结算结果为12222,同样舍去最高位数字1,元素最终的散列地址为2222。
特点:适用于长关键码,当关键码位数较多时,且关键码每一位的数字上的分布较均匀时,可使用这种方法获得哈希地址。
六、除留余数法(最常用)
以关键码除以m的余数作为哈希地址,若设计的哈希表长为n,则一般取 m ≤ n 且为质数(也可为合数,但不能包含小于20的质因子)。
公式:H(k) = k mod m(m ≤ n ,m是一个整数,n是表的长度)。
为什么要对“m”加限制?
例子:给定一个关键码集合:{12,39,18,24,33,21},若取m = 9,则他们对应小标如下表:

从上表可见:若m中含质因子3,则所有含质因子3的关键码均映射到“3的倍数”的地址上,从而增加了“哈希冲突”的可能。
如何选取合适的“m”?
若哈希表表长为n,通常m为小于或等于表长(最好接近n)的最小质数或不包含小于20质因子的合数。当P取小于哈希表长的最大质数时,产生的哈希函数较好,因为它是离长度值最近的最大质数。
例如:关键码集合{12,24,36,48,60,72,84,96,108,120,132,144}
情况1:取m=12,根据公式:H(k) = k mod m,可以得到如下表:

从上表可以看出,得到的下标都为“0”,这样就造成了“哈希冲突”了。
情况2:如果m = 11,可以得到如下表:

从上表可以看出只有12和144冲突了,相比情况1,哈希冲突变少了。
特点:除留余数法有效缩减散列地址空间的大小,是最常用的方法。
七、乘余取整法
公式:H(k)=[b ×(a × k mod 1)] ,(a,b均为常数,且0 < a < 1,b为整数),(a × k mod 1)取的是 a × k 的小数部分。
特点:以关键码k乘以a,取其小数部分,然后再放大b倍再取整,作为哈希地址。
例如,当元素关键码为2020, 小数a为0.6180339,整数b为10000,则散列地址计算为H(2020)=[10000×(0.6180339×2020%1)]=4284。
特点:乘余取整法不但会缩减散列地址空间的大小,还能极大减小冲突情况的发生几率。Knuth(唐纳德·克努特,算法和程序设计的先驱)对常数a的取法做了仔细的研究,发现虽然a取任何值都可以,但一般取黄金分割数0.6180339比较好。
八、随机函数法
选择一随机函数,取关键字的随机值作为散列地址,及H(k) = random(k),(random(k)为伪随机数方法),通常用于关键码长度不同的场合。
九、总结
(一)、上述五中实现方式中最常用的是除留余数法,而通过哈希函数寻址的过程可能出现“冲突”------即若干个不同的key却对应相同的哈希地址。解决哈希冲突可查看Java集合(九)哈希冲突及解决哈希冲突的4种方式。
(二)、构造哈希函数的原则
1、执行速度,即计算哈希函数的时间;
2、关键字的长度;
3、哈希表的大小;
4、关键字的分布情况;
5、查找频率。
Java集合(八)哈希表及哈希函数的实现方式的更多相关文章
- 哈希,哈希表,哈希Map
数组: 数组存储区间是连续的,占用内存严重,故空间复杂的很大.但数组的二分查找时间复杂度小,为O(1):数组的特点是:寻址容易,插入和删除困难: 链表: 链表存储区间离散,占用内存比较宽松,故空间复杂 ...
- 剑指 Offer 50. 第一个只出现一次的字符 + 哈希表 + 有序哈希表
剑指 Offer 50. 第一个只出现一次的字符 Offer_50 题目详情 方法一:使用无序哈希表 package com.walegarrett.offer; /** * @Author Wale ...
- Java List集合和哈希表
List集合和Set集合,先来看List集合. List集合存储元素的特点: 1.有序(List集合中的元素有下标):存进去是什么样,取出来还是什么样 2.可重复 可以结合以下的简单代码来看一看. i ...
- Java学习:Set接口与HashSet集合存储数据的结构(哈希表)
Set接口 java.util.Set接口 extends Collection接口 Set接口的特点: 不允许存储重复的元素 没有索引,没有带索引的方法,也不能使用普通的for循环遍历 java.u ...
- Java基础知识笔记(一:修饰词、向量、哈希表)
一.Java语言的特点(养成经常查看Java在线帮助文档的习惯) (1)简单性:Java语言是在C和C++计算机语言的基础上进行简化和改进的一种新型计算机语言.它去掉了C和C++最难正确应用的指针和最 ...
- java数据结构----哈希表
1.哈希表:它是一种数据结构,可以提供快速的插入操作和查找操作.如果哈希表中有多少数据项,插入和删除操作只需要接近常量的时间.即O(1)的时间级.在计算机中如果需要一秒内查找上千条记录,通常使用哈希表 ...
- 数据结构中的哈希表(java实现)利用哈希表实现学生信息的存储
哈希表 解释 哈希表是一种根据关键码去寻找值的数据映射结构,该结构通过把关键码映射的位置去寻找存放值的地方 内存结构分析图 1.定义一个类为结点,存储的信息 2.定义链表的相关操作 3.定义一个数组存 ...
- [Java集合] 彻底搞懂HashMap,HashTable,ConcurrentHashMap之关联.
注: 今天看到的一篇讲hashMap,hashTable,concurrentHashMap很透彻的一篇文章, 感谢原作者的分享. 原文地址: http://blog.csdn.net/zhanger ...
- Java集合源码学习(四)HashMap分析
ArrayList.LinkedList和HashMap的源码是一起看的,横向对比吧,感觉对这三种数据结构的理解加深了很多. >>数组.链表和哈希表结构 数据结构中有数组和链表来实现对数据 ...
随机推荐
- Android 讯飞语音听写SDK快速接入(附空指针解决和修改对话框文字方法)
1.账号准备工作 首先要有一个讯飞的账号啦,为后面申请APPID.APPKey等东西做准备.顺带一提:讯飞对不同认证类型用户开 放的SDK的使用次数是有不同的,详情如下图. 账号申请完成后,需要去你自 ...
- MySQL导出数据到文件中的方法
MySQL导出数据到文件中的方法 1.导出数据到txt文件中实例:把数据表studscoreinfo中所有数据导出到指定的位置方法:select * from 表名 into outfile 指定导出 ...
- SpringData Redis的简单使用
SpringDate Redis是在Jedis框架的基础之上对Redis进行了高度封装,通过简单的属性配置就可以通过调用方法完成对Redis数据库的操作,而且SpringData Redis使用了连接 ...
- mui日期设置与时钟样式时间设置
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name ...
- train loss与test loss结果分析/loss不下降
train loss与test loss结果分析 train loss 不断下降,test loss不断下降,说明网络仍在学习; train loss 不断下降,test loss趋于不变,说明网络过 ...
- 浅析Spring中AOP的实现原理——动态代理
一.前言 最近在复习Spring的相关内容,刚刚大致研究了一下Spring中,AOP的实现原理.这篇博客就来简单地聊一聊Spring的AOP是如何实现的,并通过一个简单的测试用例来验证一下.废话不 ...
- Web的Cookies,Session,Application
Cookies:客户端(浏览器)存储信息的地方 Session:服务器的内置对象,可以在这里存储信息.按用户区分,每个客户端有一个特定的SessionID.存储时间按分钟计. Application: ...
- [zoj 3416/hdu 3709]数位DP
题意:求从区间[L, R]内有多少个数是平衡数,平衡数是指以10进制的某一位为中心轴,左右两边的每一位到中心轴的距离乘上数位上的值的和相等.0<=L<=R<=1e18 思路:由于任何 ...
- Android 编译系统
1,Makefile编译方式 TARGET: PREREQUISITES COMMANDS 1,TARGET是需要生成的目标文件,PREREQUISTIES代表了目标所依赖的所有文件. 2,简单的Ma ...
- 【图论算法】LCA最近公共祖先问题
LCA模板题https://www.luogu.com.cn/problem/P3379题意理解 对于有根树T的两个结点u.v,最近公共祖先LCA(u,v)表示一个结点x,满足x是u.v的祖先且x的深 ...