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的源码是一起看的,横向对比吧,感觉对这三种数据结构的理解加深了很多. >>数组.链表和哈希表结构 数据结构中有数组和链表来实现对数据 ...
随机推荐
- RF(自定义关键字)
1.在 D:\work_software\python\Lib\site-packages 文件夹下, 新建 python package 包 ,例如我的是 TestLibrary 建好后的完整路径: ...
- Centos7 搭建bind9.9
DNS服务器概述: DNS(Domain Name System),即域名系统. DNS服务器分为三种: 主域名服务器(Master Server).辅助域名服务器(Slave DNS).缓存服务器( ...
- 跟哥一起学python(2)- 运行第一个python程序&环境搭建
本节的任务,是完成我们的第一个python程序,并搭建好学习python的环境. 建议通过视频来学习本节内容: 查看本节视频 再次看看上一节提到的那张图,看看作为高级编程语言,我们如何编程. 首先, ...
- Java——多线程之线程间通信
Java多线系列文章是Java多线程的详解介绍,对多线程还不熟悉的同学可以先去看一下我的这篇博客Java基础系列3:多线程超详细总结,这篇博客从宏观层面介绍了多线程的整体概况,接下来的几篇文章是对多线 ...
- Prime Path素数筛与BFS动态规划
埃拉托斯特尼筛法(sieve of Eratosthenes ) 是古希腊数学家埃拉托斯特尼发明的计算素数的方法.对于求解不大于n的所有素数,我们先找出sqrt(n)内的所有素数p1到pk,其中k = ...
- CF#132 C. Logo Turtle DP
C. Logo Turtle 题意 有一个海龟在一个x轴的0点,给出一个由'F','T'组成的字符序列. 海龟要按照这个序列进行行动,如果第i个字符为'F',表示沿当前方向走,'T'表示转身. 现在你 ...
- [hdu5200]离线+标记
思路:按顺序处理,新建一堆然后向左右合并,不过巧妙地用了标记数组来记录和统计答案. #pragma comment(linker, "/STACK:10240000,10240000&quo ...
- JS理论--正则表达式RegExp的创建、元字符、优先度、方法(test(),exec(),match(),replace())
实战,参数可以对照上面的,代码一定要敲 var str = 'abc123cba456aaa789'; var reg = /\d+/g; console.log(reg.test(str)) //s ...
- SDK,JDK,API的区别
[基础概念] 先留一波传送门: SDK:软件开发工具包(外语全称:Software Development Kit)一般都是一些软件工程师为特定的软件包.软件框架.硬件平台.操作系统等建立应用软件时的 ...
- watch 和 计算属性
作者:纵横链接:https://www.zhihu.com/question/55846720/answer/331760496来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明 ...