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集合(八)哈希表及哈希函数的实现方式的更多相关文章

  1. 哈希,哈希表,哈希Map

    数组: 数组存储区间是连续的,占用内存严重,故空间复杂的很大.但数组的二分查找时间复杂度小,为O(1):数组的特点是:寻址容易,插入和删除困难: 链表: 链表存储区间离散,占用内存比较宽松,故空间复杂 ...

  2. 剑指 Offer 50. 第一个只出现一次的字符 + 哈希表 + 有序哈希表

    剑指 Offer 50. 第一个只出现一次的字符 Offer_50 题目详情 方法一:使用无序哈希表 package com.walegarrett.offer; /** * @Author Wale ...

  3. Java List集合和哈希表

    List集合和Set集合,先来看List集合. List集合存储元素的特点: 1.有序(List集合中的元素有下标):存进去是什么样,取出来还是什么样 2.可重复 可以结合以下的简单代码来看一看. i ...

  4. Java学习:Set接口与HashSet集合存储数据的结构(哈希表)

    Set接口 java.util.Set接口 extends Collection接口 Set接口的特点: 不允许存储重复的元素 没有索引,没有带索引的方法,也不能使用普通的for循环遍历 java.u ...

  5. Java基础知识笔记(一:修饰词、向量、哈希表)

    一.Java语言的特点(养成经常查看Java在线帮助文档的习惯) (1)简单性:Java语言是在C和C++计算机语言的基础上进行简化和改进的一种新型计算机语言.它去掉了C和C++最难正确应用的指针和最 ...

  6. java数据结构----哈希表

    1.哈希表:它是一种数据结构,可以提供快速的插入操作和查找操作.如果哈希表中有多少数据项,插入和删除操作只需要接近常量的时间.即O(1)的时间级.在计算机中如果需要一秒内查找上千条记录,通常使用哈希表 ...

  7. 数据结构中的哈希表(java实现)利用哈希表实现学生信息的存储

    哈希表 解释 哈希表是一种根据关键码去寻找值的数据映射结构,该结构通过把关键码映射的位置去寻找存放值的地方 内存结构分析图 1.定义一个类为结点,存储的信息 2.定义链表的相关操作 3.定义一个数组存 ...

  8. [Java集合] 彻底搞懂HashMap,HashTable,ConcurrentHashMap之关联.

    注: 今天看到的一篇讲hashMap,hashTable,concurrentHashMap很透彻的一篇文章, 感谢原作者的分享. 原文地址: http://blog.csdn.net/zhanger ...

  9. Java集合源码学习(四)HashMap分析

    ArrayList.LinkedList和HashMap的源码是一起看的,横向对比吧,感觉对这三种数据结构的理解加深了很多. >>数组.链表和哈希表结构 数据结构中有数组和链表来实现对数据 ...

随机推荐

  1. Python基础01 集合

    初始化 # python3 # coding = utf-8 mylist = [] for item in range(10): mylist.append(item * 10 + 3) myset ...

  2. 2018 USP-ICMC

    简单题 B D F L 中等难度题 E I 更难一点得题 A C G 难题 H K J B. Ugly Number 这个题目很简单,不过我的方法有点点小问题,不过可以改进一下就应该没什么问题了. 这 ...

  3. P3355 骑士共存问题 网络流

    骑士共存 题目描述 在一个 n*n个方格的国际象棋棋盘上,马(骑士)可以攻击的棋盘方格如图所示.棋盘上某些方格设置了障碍,骑士不得进入 对于给定的 n*n 个方格的国际象棋棋盘和障碍标志,计算棋盘上最 ...

  4. ubuntu docker相关错误记录

    执行下面命令 curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - 报错: gpg: can't c ...

  5. 解决Eclipse添加新server时无法选择Tomcat7.0

    新添加tomcat时 出现如下图情况: 解决方法:这时打开工作空间目录下的.metadata\.plugins\org.eclipse.core.runtime\.settings文件夹,删除org. ...

  6. Codeforces Round #643 (Div. 2)(C ~ E)

    C. Count Triangles 题目链接 : https://codeforces.com/contest/1355/problem/C 题目大意 : 给你 A , B , C , D 问有多少 ...

  7. 基于hexo创建博客(Github托管)

    基于hexo的博客 搭建好的博客网站 dengshuo7412.com 搭建步骤 1.依赖文件下载 Node.js 2.Hexo的安装 3.部署到Github 4.Hexo创建博客基本操作 5.Hex ...

  8. RocketMQ Windows 搭建

    一.rocketMQ 下载 官网:http://rocketmq.apache.org/ 本人使用是v4.3.0版本,百度网盘下载地址链接:https://pan.baidu.com/s/1qWewB ...

  9. 「从零单排HBase 12」HBase二级索引Phoenix使用与最佳实践

    Phoenix是构建在HBase上的一个SQL层,能让我们用标准的JDBC APIs对HBase数据进行增删改查,构建二级索引.当然,开源产品嘛,自然需要注意“避坑”啦,阿丸会把使用方式和最佳实践都告 ...

  10. 【雕爷学编程】Arduino动手做(61)---电压检测传感器

    37款传感器与执行器的提法,在网络上广泛流传,其实Arduino能够兼容的传感器模块肯定是不止这37种的.鉴于本人手头积累了一些传感器和执行器模块,依照实践出真知(一定要动手做)的理念,以学习和交流为 ...