哈希算法又称散列函数算法,是一种查找算法。
简单来说,就是把一些复杂的数据,通过某种函数映射关系,映射成一种易于查找的方式。
但这种映射关系有可能会发生多个关键字映射到同一地址的现象,称之为冲突,需要对关键字进行二次或多次处理。
- 什么是哈希: 哈希和哈希原理
- 两个数的和: 快速寻找两个数的和(链表与哈希比较)
- 单词模式匹配: 简单的模式匹配问题(使用哈希--思维方式)
- 猜词游戏: (使用哈希--效率)
- 神奇的词根: (使用哈希--效率)
什么是哈希
常见的数据查找算法
-
顺序查找
最简单的查找方式,需要对数据逐个进行匹配,所以效率相对较低,不适合大量数据查找。 -
二分查找
二分查找效率很高,但是要求数据必须有序,而对数据进行排序通常需要很多的时间开销。 -
深度优先遍历
对于大数据量的查找问题效率并不高 -
广度优先遍历
同深度优先遍历,对于大数据量的查找问题效率并不高 -
哈希查找
由于查找速度快,查询、插入、删除操作简单等原因被广泛应用。
哈希算法
哈希算法进行查找的基本原理是根据数量 预先设置一个长度为M的数组,使用一个哈希函数F,并以数据的关键字作为自变量,得到唯一的返回值,返回值的范围为[0, M-1],这样就可以利用哈希函数F将数据元素映射到数组的某一位下标并把数据存放在对应的位置上。
哈希是一种高效的存储算法,也是一种高效的查找算法。
哈希像一本字典,当进行查词的时候,通过目录找到页码,再到对应页码就能找到所需要的内容
# 难点
一般情况下,哈希算法的查询效率可以达到常数级别,哈希表成为直接寻址的有效替代方案。而有时候关键字的取值范围太大,数据在通过函数进行映射的时候,找不到一个哈希函数,使得关键字不能映射唯一的值,出现冲突现象。
# 解决哈希冲突的方法
1. 链地址法
2. 二次再散列法
3. 线性探测再散列
4. 建立一个公共溢出区
...
两个数的和
问题: 给出一些数,1,3, 4, 5, 7 选择两个数使他们的和为: 11
问题求解1
使用双指针的方法解决。
为了找到两个元素的和为目标值,先将数组从小到大排序。由于最后得到的是两个数原有的编号,而非两个数本身,因此需要保留原来数据的下标。
两个数的和解法1
def two_sum(list, target):
res = []
new_list = list[:]
new_list.sort()
# 定义left、right 指针分别指向新数组的开头和结尾
left = 0
right = len(new_list) - 1
while left < right:
if new_list[left] + new_list[right] == target:
for i in range(len(list)):
if list[i] == new_list[left]:
res.append(i)
break
for i in range(len(list)-1, -1, -1):
if list[i] == new_list[right]:
res.append(i)
break
res.sort()
break
elif new_list[left] + new_list[right] < target:
# 让left 指针向右移一位
left += 1
elif new_list[left] + new_list[right] > target:
# 让right 指针向左移一位
right -= 1
return res
# 所有元素
lst = [1, 3, 4, 5, 7]
# 目标值
target = 11
# 获取存放结果编号数据
res = two_sum(lst, target)
print('所有数中和为11的元素:', [lst[i] for i in res])
问题求解2
使用哈希算法解决
使用哈希算法,要先建立一个字典,用于存放数据和下标的对应关系。
使用字典记录数据,再去字典中查找数据的方式就是哈希方法
两个数的和解法2
def two_sum(list, target):
dict = {}
for i in range(len(list)):
m = list[i]
if target-m in dict:
return (dict[target-m], i)
dict[m] = i
# 所有元素
lst = [1, 3, 4, 5, 7]
# 目标值
target = 11
di = two_sum(lst, target)
print('所有数中和为11的元素:', [lst[i] for i in di])
单词匹配模式
模式匹配问题是一个经典问题
先研究一个简单的单词匹配模式。首先给定两个字符串,一个是单词模式字符串,另一个是目标字符串。之后检测目标字符串是否为给定的单词模式,即求目标字符串中单词出现的规律是否符合单词模式字符串中的规律。
映射关系有
- 一对一关系
- 一对多关系
- 多对多关系
问题求解
单词模式字符串和目标字符串 只存在一对一的对应关系,就可以说明两个字符串匹配成功。
问题其实已经转换为寻找映射关系,本质上也是一个查找问题。
1. 首先建立哈希表来存储数据
result: 用来存储 模式字符串 和 目标字符串 的对应关系
used: 用来记录已经使用的字符串
2. 解决模式字符串中的每个字符只能对应目标字符串一个单词的问题。
每次拿到模式字符串中的一个字符的时候,需要检查一下它是否已经被记录过映射关系,若不是一对一的映射关系,则返回不成立,若是第一次出现,就把它存储在result哈希表中
3. 解决当模式字符串中的某一个字符第一次出现
需要判断这个单词是否已经和其他的模式字符绑定,需要用到used哈希表
单词模式匹配问题
def word_pattern(words_list, input_str):
# 将目标字符串以','切割
word = input_str.split(',')
# 若两个字符串的长度不一样,直接匹配失败
if len(words_list) != len(word):
return False
# 记录模式字符串和目标字符串的对应关系
result = {}
# 记录目前已经使用过的字符串有哪些
used = {}
# 检测
for i in range(len(words_list)):
# 检测模式字符串中的字符是否已经被记录过映射关系
if words_list[i] in result:
# 不是第一次出现, 检测映射关系是否一致
if result[words_list[i]] != word[i]:
return False
pass
else:
# 检测 这个单词是否已经用过
if word[i] in used:
return False
# 第一次出现, 则加入哈希表
result[words_list[i]] = word[i]
# 在used哈希表中记录哪些已经使用过
used[word[i]] = True
return True
words_list = ['aaa', 'bbb', 'aaa']
input_str1 = 'aaa,bbb,ccc'
input_str2 = 'ccc,ooo,ccc'
res1 = word_pattern(words_list, input_str1)
print(res1)
res2 = word_pattern(words_list, input_str2)
print(res2)