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的源码是一起看的,横向对比吧,感觉对这三种数据结构的理解加深了很多. >>数组.链表和哈希表结构 数据结构中有数组和链表来实现对数据 ...
随机推荐
- Linux文件删除空间未释放
当系统空间使用量过大需要清理空间或者清理某个文件时,有时会出现执行了删除命令之后磁盘空间并没有释放,很多人首次遇到该情况时会比较困惑,在考虑是不是像windows系统的回收站一样,删除只是逻辑删除到回 ...
- RDP凭据离线脱取
内网渗透-获取本地RDP密码 获取RDP 连接记录: reg query "HKEY_CURRENT_USER\Software\Microsoft\Terminal Server Clie ...
- Spring官网阅读(十一)ApplicationContext详细介绍(上)
文章目录 ApplicationContext 1.ApplicationContext的继承关系 2.ApplicationContext的功能 Spring中的国际化(MessageSource) ...
- 实时(RTC)时钟,系统时钟和CPU时钟
最近在学stm32的时候看到RTC时钟和系统时钟,不知道区别在哪里,于是上网查了一下. 实时时钟:RTC时钟,用于提供年.月.日.时.分.秒和星期等的实时时间信息,由后备电池供电,当你晚上关闭系统和早 ...
- Directory类和DirectoryInfo类
1.Directory类 Directory类公开了用于创建.移动.枚举.删除目录和子目录的静态方法 2.DirectoryInfo类 DirectoryInfo和Directory类的区别可以参看F ...
- 自己动手在Linux系统实现一个everything程序
大家好,我是良许. 我们知道,在 Windows 下,有一款非常实用的神器,叫作 Everything ,它可以在极短的时间里,搜索出来你所想要的文件/目录,如下图示: Linux 下也有一些类似于 ...
- 我的第一篇博客-学习书写markdown
Markdown学习(标题:井号+空格+标题名字 回车 ) 标题: 二级标题## 空格+名字 三级标题### 空格+名字 四级标题#### 空格+名字 五级标题##### 空格+名字 六级标题#### ...
- 读懂操作系统(x86)之堆栈帧(过程调用)
前言 为进行基础回炉,接下来一段时间我将持续更新汇编和操作系统相关知识,希望通过屏蔽底层细节能让大家明白每节所阐述内容.当我们写下如下C代码时背后究竟发生了什么呢? #include <stdi ...
- Python-MD5加密算法接口测试
前言 在之前的Python系列的随笔中,我跟小伙伴们讲述了Python-AES加密算法接口测试和Python-SHA256加密算法接口测试,今天我们来学习一下登录密码是md5加密的情况下,该如何使用p ...
- Kafka面试你不得不知道的基础知识
Java内存管理面试指南一 Java基础面试指南一 Java基础面试指南二 Java基础面试指南三 Java基础面试指南四 Java线程面试指南一 Java线程面试指南二 Redis面试指南一 Kaf ...