哈希算法又称散列函数算法,是一种查找算法。就是把一些复杂的数据通过某种映射关系。映射成更容易查找的方式,但这种映射关系可能会发生多个关键字映射到同一地址的现象,我们称之为冲突。在这种情况下,我们需要对关键字进行二次或更多次处理。出这种情况外,哈希算法可以实现在常数时间内存储和查找这些关键字。
常见的数据查找算法:
顺序查找:是最简单的查找方法。需要对数据集中的逐个匹配。所以效率相对较低,不太适合大量数据的查找问题。
二分法查找:效率很高,但是要求数据必须有序。面对数据排序通常需要更多的时间。
深度优先和广度优先算法:对于大量的数据查找问题,效率并不高。这个我们后面专门讲解。
阿希查找算法:查找速度快,查询插入,删除操作简单等原因获得广泛的应用。
哈希查找的原理:根据数量预先设一个长度为M的数组。使用一个哈希函数F并以数据的关键字作为自变量得到唯一的返回值,返回值的范围是0~M-1。这样就可以利用哈希函数F将数据元素映射到一个数组的某一位下标,并把数据存放在对应位置,查找时利用哈希函数F计算,该数据应存放在哪里,在相应的存储位置取出查找的数据。
这里就有一个问题:
关键字的取值在一个很大的范围,数据在通过哈希函数进行映射时。很难找到一个哈希函数,使得这些关键字都能映射到唯一的值。就会出现多个关键字映射到同一个值的现象,这种现象我们称之为冲突。
哈西算法冲突的解决方案有很多:链地址法,二次再散列法。线性探测再散列建立一个公共溢出区
注意:链地址法本质是数组+链表的数据结构
链地址法存储数据过程:
首先建立一个数组哈希存储所有链表的头指针。由数组的关键字key通过对应的哈希函数计算出哈希地址。找到相应的桶号之后,建立新的节点存储该数据。并把节点放到桶内的链表的最后面或者最前面。
链地址法查找数据:由数据关键字通过哈希。函数计算关键字对应的哈希地址之后顺序比较同类不节点。是否与所查到的关键字一样,直到找到数据为止,如果全部节点都不和关键字一样,则书名哈系表里没有该数据。解决了哈希函数的冲突。
用链地址法构造的散列表插入和删除节点操作易于实现,所以构造链表的时间开销很低。但是指针需要开辟额外的地址空间,当数据量很大时会扩大哈希表规模,内存空间要求较大。
要求在给定的一些数字中找出两个数,使得它们的和为嗯,前提是这些数据中保证有答案,并且只有一个答案。例如给3、4、5、7、10。从中选出两个数字使它们的和为11,可以选择4和7。
def twosum(nums,target):
res = [] #存放结果编号
newnumber = nums [:] #将数据深拷贝一份
newnumber.sort() #对拷贝结果排序
left = 0 #定义左指针
right = len(newnumber) -1 # 定义右指针
while left < right:
if newnumber[left] + newnumber[right] ==target: #在原始数组中第一个元组寻找原始下标
for i in range(0,len(nums)):
if nums[i] == newnumber[left]:
res.append(i) #将下标结果接入集
break
for i in range(len(nums) - 1 , -1 , -1):#向回找,寻找第二个元组
if nums[i] == newnumber[right]:
res.append(i)
break
res.sort()
break
elif newnumber[left] +newnumber[right] < target:
left = left +1
else :
right = right -1
return (res[0] + 1 ,res[1] + 1 )
s =twosum([3,4,5,7,10],11)
print('下标是:',s)
双指针解决方案:
第一步:对数据进行排序
第二步:移动指针寻找答案
第三步:发现答案去原始数据中查找这两个元素的位置
显然第一步和第三步很浪费时间,我们使用哈希算法,规避排序问题,直接查找数据和下标的对应关系。
def twosum(nums,target):
'''
这个函数只能解决两个数字和,且答案有且仅有一个的情况
'''
dict = {}
for i in range(len(nums)):
m = nums[i]
if target - m in dict:#判定target - m 是否在字典中
return (dict[target-m]+1,i +1) #存在返回连个数的下标
dict[m] = i #若不存在则记录键值对的值
s =twosum([3,4,5,7,10],11)
print('下标是:',s)
字典使用dict[key] = value 来记录键值对的关系