1.认识哈希函数和哈希表
哈希函数及其重要,在大数据的题目中常考。下边介绍几个哈希函数相关的概念:
1.1什么是 Hash
Hash(哈希),又称“散列”。
散列(hash)英文原意是“混杂”、“拼凑”、“重新表述”的意思。
在某种程度上,散列是与排序相反的一种操作,排序是将集合中的元素按照某种方式比如字典顺序排列在一起,而散列通过计算哈希值,打破元素之间原有的关系,使集合中的元素按照散列函数的分类进行排列。
在介绍一些集合时,我们总强调需要重写某个类的 equlas() 方法和 hashCode() 方法,确保唯一性。这里的 hashCode() 表示的是对当前对象的唯一标示。计算 hashCode 的过程就称作 哈希。
1.2为什么要有 Hash
我们通常使用数组或者链表来存储元素,一旦存储的内容数量特别多,需要占用很大的空间,而且在查找某个元素是否存在的过程中,数组和链表都需要挨个循环比较,而通过 哈希 计算,可以大大减少比较次数。
1.3举个栗子:
现在有 4 个数 {2,5,9,13},需要查找 13 是否存在。
1.使用数组存储,需要新建个数组 new int[]{2,5,9,13},然后需要写个循环遍历查找:
int[] numbers = new int[]{2,5,9,13};
for (int i = 0; i < numbers.length; i++) {
if (numbers[i] == 13){
System.out.println("find it!");
return;
}
}
这样需要遍历 4 次才能找到,时间复杂度为 O(n)。
2.而假如存储时先使用哈希函数进行计算,这里我随便用个函数:
H[key] = key % 3;
四个数 {2,5,9,13} 对应的哈希值为:
H[2] = 2 % 3 = 2;
H[5] = 5 % 3 = 2;
H[9] = 9 % 3 = 0;
H[13] = 13 % 3 = 1;
然后把它们存储到对应的位置。
当要查找 13 时,只要先使用哈希函数计算它的位置,然后去那个位置查看是否存在就好了,本例中只需查找一次,时间复杂度为 O(1)。
因此可以发现,哈希 其实是随机存储的一种优化,先进行分类,然后查找时按照这个对象的分类去找。
哈希通过一次计算大幅度缩小查找范围,自然比从全部数据里查找速度要快。
比如你和我一样是个剁手族买书狂,家里书一大堆,如果书存放时不分类直接摆到书架上(数组存储),找某本书时可能需要脑袋从左往右从上往下转好几圈才能发现;如果存放时按照类别分开放,技术书、小说、文学等等分开(按照某种哈希函数计算),找书时只要从它对应的分类里找,自然省事多了。
1.4哈希函数
哈希的过程中需要使用哈希函数进行计算。
哈希函数是一种映射关系,根据数据的关键词 key ,通过一定的函数关系,计算出该元素存储位置的函数。
表示为:
address = H [key]
哈希函数的实现等查看:哈希函数
1.5 哈希函数的特点:
- 1)Hash可用于任意大小的数据块;
- (2)hash可以接受任意长度的信息,并将其输出成固定长度的消息摘要;
- (3)单向性。给定一个输入M,一定有一个h与其对应,满足H(M)=h,反之,则不行,算法操作是不可逆的。
- (4)抗碰撞性。给定一个M,要找到一个M’满足是不可H(M)=H(M’)是不可能的。即不能同时找到两个不同的输入使其输出结果完全一致。
- (5)低复杂性:算法具有运算的低复杂性。
1.6 哈希表常用的功能演示
代码:
//哈希表的增删改查的复杂度是近似于O(1),实际应该是O(log5N之类的形式)
public class Code_01_HashMap {
public static void main(String[] args) {
HashMap<String, String> map = new HashMap<>();
map.put("wang", "31");
System.out.println(map.containsKey("wang"));
System.out.println(map.containsKey("laowu"));
System.out.println("=========================");
System.out.println(map.get("wang"));
System.out.println(map.get("laowu"));
System.out.println("=========================");
System.out.println(map.isEmpty());
System.out.println(map.size());
System.out.println("=========================");
System.out.println(map.remove("wang"));
System.out.println(map.containsKey("wang"));
System.out.println(map.get("wang"));
System.out.println(map.isEmpty());
System.out.println(map.size());
System.out.println("=========================");
map.put("wang", "31");
System.out.println(map.get("wang"));
map.put("wang", "32");
System.out.println(map.get("wang"));
System.out.println("=========================");
map.put("wang", "31");
map.put("lao", "32");
map.put("wu", "33");
for (String key : map.keySet()) {
System.out.println(key);
}
System.out.println("=========================");
for (String values : map.values()) {
System.out.println(values);
}
System.out.println("========================="