leetcode算法学习-哈希

171 阅读4分钟

1.概念

  1. 存在的意义: 正常计算机存储都是有序的,我们通常使用数组或者链表来存储元素,一旦存储的内容数量特别多,需要占用很大的空间,而且在查找某个元素是否存在的过程中,数组和链表都需要挨个循环比较,而通过 哈希 计算,可以大大减少比较次数。但是会占用额外的空间,是利用空间换时间。

  2. 哈希(hash):也叫散列,是电脑科学中一种对资料的处理方法,通过某种特定的函数/算法(称为散列函数/算法)将要检索的项与用来检索的索引(称为散列,或者散列值)关联起来,生成一种便于搜索的数据结构(称为散列表)。存储是无序的。

  3. 哈希表又叫散列表,哈希表就是通过一个映射函数f(key)将一组数据散列存储在数组中的一种数据结构。在这哈希表中,每一个元素的key和它的存储位置都存在一个f(key)的映射关系,我们可以通过f(key)快速的查找到这个元素在表中的位置。

  4. 哈希表存储的每一个单一即桶(bucket)

2.例子

2.1数组

已知:数组Array [14,23,67,5,89,90],需要检查89是否存在 数组里面?

  • 答1:直接遍历整个数组array,【复杂度是 O(n),空间复杂度O(0)】
  • 答2:通过f(x)哈希函数,89% 10 == 9. 得到索引9,然后去hashTable[9],得到89,证明存在。 【复杂度是 O(n),空间复杂度O(n)】

image.png

2.2对象,键值关系

已知:对象 obj = {'name':'jason','age':18,'sex':'男',address:'深圳湾1号'},求对象name的值

  • 答:首先把name对应的每个字母,转化成对应的ascii,然后相加等到345,再求余得到索引5,从hashTable[5] 得到 jason

image.png

3.哈希冲突

选用哈希函数计算哈希值时,可能不同的 key 会得到相同的结果,一个地址怎么存放多个数据呢?这就是冲突。

1.链地址法 - 链接法(拉链法)

将所有关键字为同义词的结点链接在同一个单链表中。

已知:数组Array [14,23,67,5,89,90,93,3],需要检查93是否存在 数组里面?

  • 答2:通过f(x)哈希函数,93% 10 == 3. 得到索引3,然后去hashTable[3],得到23,不是93,找当前链表下个节点93,发现是,则存在。

image.png

1.1当键值对象冲突

image.png

2.开放定址法

a.线性探查法

hi=(h(key)+i) % m ,0 ≤ i ≤ m-1

探查时从地址 d 开始,首先探查 T[d],然后依次探查 T[d+1],…,直到 T[m-1],此后又循环到 T[0],T[1],…,直到探查到 有空余地址 或者到 T[d-1]为止。

b.二次探查法

hi=(h(key)+i*i) % m,0 ≤ i ≤ m-1

探查时从地址 d 开始,首先探查 T[d],然后依次探查 T[d+1^2],T[d+2^2],T[d+3^2],…,等,直到探查到 有空余地址 或者到 T[d-1]为止。

缺点:无法探查到整个散列空间。

c.双重散列法

hi=(h(key)+i*h1(key)) % m,0 ≤ i ≤ m-1

探查时从地址 d 开始,首先探查 T[d],然后依次探查 T[d+h1(d)], T[d + 2*h1(d)],…,等。

该方法使用了两个散列函数 h(key) 和 h1(key),故也称为双散列函数探查法。

定义 h1(key) 的方法较多,但无论采用什么方法定义,都必须使 h1(key) 的值和 m 互素,才能使发生冲突的同义词地址均匀地分布在整个表中,否则可能造成同义词地址的循环计算。

该方法是开放定址法中最好的方法之一。

例子

已知:数组Array [14,23,67,5,89,90,93,3],需要检查93是否存在 数组里面?

  • 答2:通过f(x)哈希函数,93 % 10 == 3. 得到索引3,然后去hashTable[3],得到23,不是93,往下走 93 找到第6格,存放了93,找到。

image.png

其他优化方法

  1. 优化哈希函数,让他更均匀分布,
  2. 扩容,增加哈希表的长度