【数据结构】-哈希函数、哈希值、哈希表
什么是哈希函数?
(哈希函数,又称散列算法,英语:Hash Function)是一种从任何一种数据中创建小的数字“指纹”的方法。散列函数把消息或数据压缩成摘要,使得数据量变小,将数据的格式固定下来。 在java的String类中有hashcode方法: 当然Integer,boolean,对象等都有自己的哈希函数。 那么哈希函数的有几种构造方法呢?
-
上图是String类的哈希函数,计算出来的哈希值与字符串的字符个数和ASCII码有关。由此计算出的哈希值必唯一。 除此之外还有随机乘数法、ASCII码加和法、移位法。 还有直接定址法、除留余数、平方取中。
哈希值
哈希值又称哈希码,是哈希函数计算出来,表示一个对象的值。
-
`如果两个哈希值是不相同的(根据同一函数),那么这两个散列值的原始输入一定是不相同的。 相同的哈希函数计算出来的哈希值是不一定唯一`的,如果两个字符串的哈希值相同,那么字符串一定是相同的。但如果用除留余数法计算出来的哈希值,哈希值相同,输入值却不同。 如果两个哈希值相同,两个输入值很可能(极大概率)是相同的,但也可能不同,这种情况称为“哈希碰撞”。
哈希表
哈希表的由来
-
链表是一个能够快速插入删除的数据结构,但是查找需要遍历。 数组是一个找到比较快的数据结构,但是必须知道数组的下标,如果不知道数组的下标,依然需要遍历。但是插入和删除也是比较麻烦。
由上述的两个问题,能不能找到一个快速插入删除、不需要数组下标的数据结构-------哈希表
散列表(Hash table,也叫哈希表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。
哈希表怎么创建的?
数组是知道下标后,直接找到下标对应的值。在校园中,我们如果知道一个学生的学号,那么可以很快找到他的名字。那么基于这种知道学号就可以找出对应学生的姓名怎么实现呢? 哈希函数计算哈希值 我们的学号是唯一的,并且是一个int型的数字。我们可以根据哈希函数(除留余数法)计算出哈希值, 并以哈希值为数组下标,存储名字。 由此我们可以根据关键码值(key-value)来进行访问。
-
所以哈希表,也是采用了数组的思想,将key映射到数组中的位置。如何映射的呢?通过哈希函数计算哈希值。
但是问题来了,通过除留余数法(除以13)计算出来的哈希值相同怎么办?比如学号是5,则哈希值为5。那么序号为18,哈希值也为5。这样就产生了哈希碰撞。
哈希碰撞
回到前面,我们创建哈希表的原因是数组和链表并不能很好的满足查找一个元素(知道学号,怎么知道名字(这个例子有点憨批))。计算机的资源是有限的,不可能去创建一个很大的数组供我们去查询。在缩短数组长度,并且进行存储。肯定会有碰撞的时候。当然这个问题可以解决,如下。 哈希碰撞是可以解决的,比如用开放地址法,链地址法。
链地址法
-
我们将相同的哈希值的元素,放在一个链表上,这样不同哈希值也就有不同的链表,我们将这些链表拼成一个数组。 这就是链表+数组创建的哈希表,解决了哈希碰撞的问题。
哈希查找的高效率也是有一定代价的,具体来说它是一种以空间换时间的查找算法,哈希表的构造需要合适的哈希函数,在构造哈希表时要分配跟多的空间来给记录,在记录发生冲突的时候,我们就需要一些冲突的处理方法。哈希表的构造过程也会因此更加复杂。