哈希函数
特点:
- 输入无穷大,输出有限
- 没有随机的成分,输入相同每次一定导致一样的输出
- 可能出现不同的输入导致相同的输出
- 输出域的结果是离散均匀分布的
JAVA中Hash算法的使用:
import javax.xml.bind.DatatypeConverter;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.Security;
public class HashAlg {
public static void main(String[] args) throws NoSuchAlgorithmException {
System.out.println("支持的算法");
for (String alg :
Security.getAlgorithms("MessageDigest")) {
System.out.println(alg);
}
MessageDigest hash = MessageDigest.getInstance("SHA");
String str = "1";
String ans = DatatypeConverter.printHexBinary(hash.digest(str.getBytes()));
System.out.println(ans);
}
}
经典哈希表的设计
特点
- 哈希表里面存的是一个个桶
- 一个桶的元素通常用链表相连(看具体不同的实现)
- 当存一个key的时候,我们算出这个key的hash值,然后给这个hash值模桶数,算出来的这个值就是存放的具体桶的坐标,因为hash函数的特征,key是均匀离散的分布在桶上的
- 当不同的key算出同样的hash值,就会出现hash冲突,我们就把新加进来的key放在具体存放的桶的链表的尾部(可以防止死链的发生)
- 当链表长度超过一定值的时候,我们通过hash函数算出桶的位置还需要遍历链表,这个时候时间复杂度会变大,于是当某个桶的链表超过了某个预设的长度,我们就要对哈希表进行扩容(两倍),扩容的时候要把旧桶的所有函数算一遍hash值然后模上新桶数量,从而放到新桶中
时间复杂度计算
- 我们假设给一个元素算Hash值的代价为O(1)
- 扩容代价:当我们每次链表长度超过预设值就进行桶数两倍的扩容,然后把所有key重新hash一下,那么每次扩容的代价就是O(哈希表中元素的个数),如果哈希表中初始元素个数为1,那么他每次扩容的时候的元素个数变化大致为:1,2,4,8,16,32…n 。 他是一等比数列,所以他的扩容代价为O(n),而我们一共加进来n个元素,我们把这个代价均分给每个元素,时间复杂度就是O(N)/N,即O(1)
布隆过滤器
- 利用哈希函数的性质
- 每一条数据提取特征
- 加入描黑库
引入
问题:我们有一个100亿的黑名单url,每个黑名单不超过64字节,我们拿着一个过来的url,如何判断它在不在这个黑名单里面
我们可以使用布隆过滤器解决这个类似黑名单的问题,它可以极大的节省空间,但是它一定有失误率,这个失误率指:如果我们的url不在黑名单里,那么可能会出现误报,但是如果这个url是在url里的,那么它会百分之百是正确的过滤掉
布隆过滤器相关的公式如下: