哈希函数的性质
哈希函数又名散列函数,对于经典哈希函数来说,它具有以下5点性质:
- 1、输入域无穷大
- 2、输出域有穷尽
- 3、输入一样输出肯定一样
- 4、当输入不一样输出也可能一样(哈希碰撞)
- 5、不同输入会均匀分布在输出域上(哈希函数的散列性)
如何生成多个哈希函数
这里我们介绍一种快速生成多个哈希函数的方法。
假如你急需要1000个哈希函数,并且这1000个哈希函数都要求相互独立,不能有相关性。这时,错误的方法是去在网上寻找1000个哈希函数。我们可以通过一个哈希函数来生成这样的1000个独立的哈希函数。
假如,你有一个哈希函数f,它的输出域是2^64,也就是16字节的字符串,每个位置上是16进制的数字0-9,a-f。
我们将这16字节的输出域分为两半,高八位,和低八位是相互独立的(这16位都相互独立)。这样,我们将高八位作为新的哈希函数f1的输出域,低八位作为新的哈希函数f2的输出域,得到两个新的哈希函数,它们之间相互独立。
故此可以通过以下算式得到1000个哈希函数:
f1+2f2=f3
f1+3f2=f4
f1+3*f2=f5
……
Hash表
哈希表的经典结构
在数据结构中,哈希表最开始被描述成一个指针数组,数组中存入的每个元素是指向一个链表头部的指针。
我们知道,哈希表中存入的数据是key,value类型的,哈希表能够put(key,value),同样也能get(key,value)或者remove(key,value)。当我们需要向哈希表中put(插入记录)时,我们将key拿出,通过哈希函数计算hashcode。假设我们预先留下的空间大小为16,我们就需要将通过key计算出的hashcode模以16,得到0-15之间的任意整数,然后我们将记录挂在相应位置的下面(包括key,value)。
注意:位于哪个位置下只与key有关,与value无关
例如我们要将下面这样一条记录插入哈希表中:
“shiyanlou”,666 #key是shiyanlou,value是666
首先我们通过哈希函数,计算shiyanlou的hashcode,然后模以16。假如我们得到的值是6,哈希表会先去检查6位置下是否存在数据。如果有,检查该节点中的key是否等于shiyanlou,如果等于,则将该节点中的value替换为666;如果不等于,则在链表的最后新添加一个节点,保存我们的记录。
由于哈希函数的性质,得到的hashcode会均匀分布在输出域上,所以模以16,得到的0-15之间的数目也相近。这就意味着我们哈希表每个位置下面的链表长度相近。
对于常见的几种数据结构来说,数组的特点是:容易寻址,但是插入和删除困难。而链表的特点是:寻址困难,但是插入和删除容易。而对于哈希表来说,它既容易寻址,同样插入和删除容易,这一点我们从它的数据结构中是显而易见的。
在实际哈希表应用中,它的查询速度近乎O(1),这是因为通过key计算hashcode的时间是常数项时间,而数组寻址的时间也是常数时间。在实际应用中,每个位置的链表长度不会太长,当到达一定长度后,哈希表会经历一次扩容,这就意味着遍历链表的时间也是常数时间。
所以,我们增删改查哈希表中的一条记录的时间可以默认为O(1)。
参考https://blog.csdn.net/qq_38180223/article/details/80911868