LeetCode---哈希表
哈希表
哈希表是根据关键码的值进行访问的数据结构,例如:python中的dict。list也可以称为hash表,可以通过下标直接访问某个元素。
哈希表能解决的问题
一般哈希表都是用来快速判断一个元素是否出现集合里。例如要查询一个名字是否在这所学校里。要枚举的话时间复杂度是O(n),但如果使用哈希表的话, 只需要O(1) 就可以做到。
只需要初始化把这所学校里学生的名字都存在哈希表里,在查询的时候通过索引直接就可以知道这位同学在不在这所学校里了。将学生姓名映射到哈希表上就涉及到了「hash function ,也就是哈希函数」。
哈希碰撞
如图所示,通过哈希函数计算,小李和小王都映射导了哈希表的下标为1的位置,这就导致同一个key上出现了多个value,即哈希碰撞。
哈希碰撞的解决办法
线性探测 Linear Probing
- 如下图,通过哈希函数h计算h(77)=0,故将77插入到下标为0的位置。
- 计算h(44)=0,但是下标为0#的位置已经被77占据,故向后找到下标为1#的空位。
- 计算h(55)=0,同理发现下标0#被77占据,向后找1也被44占据,继续向后找到下标为2#的位置。
- 计算h(20)=9,发现9#已经被31占据,向后找,再从头开始找到下标为3#的空位。
注意:如果通过h计算的位置没有找到查找项的话,就需要向后顺序查找,直到找到一个空位即查找失败。
拉链法
将原本只能容纳一个元素的槽位扩展为容纳数据项的集合,这样每个位置就能容纳多个元素,查找数据项时就需要查找同一个槽中的整个集合。
LeetCode
242. 有效的字母异位词
解
# 时间复杂度: O(len(s+t))
# 空间复杂度: O(s_hash)
def isAnagram(s, t):
# 遍历s,获取每个字母在s中的个数
s_hash = {}
for i in s:
if i not in s_hash:
s_hash[i] = 1
else:
s_hash[i] += 1
# 遍历t,如果字母没有在s_hash中返回False;如果在则-1.
for i in t:
if i not in s_hash:
return False
else:
s_hash[i] -= 1
# 等于0时则删除key
if s_hash[i] == 0:
s_hash.pop(i)
return s_hash == {}
第349题. 两个数组的交集
解
def intersection(nums1, nums2):
nums1_hash = {}
result = []
for i in nums1:
if i not in nums1_hash:
nums1_hash[i] = 1
else:
nums1_hash[i] += 1
for i in nums2:
if i in nums1_hash and i not in result:
result.append(i)
return result
350. 两个数组的交集 II
解
result = []
hash_map1 = {}
for num in nums1:
if num not in hash_map1:
hash_map1[num] = 1
else:
hash_map1[num] += 1
hash_map2 = {}
for num in nums2:
if num not in hash_map2:
hash_map2[num] = 1
else:
hash_map2[num] += 1
for num, count in hash_map1.items():
if hash_map2.get(num):
if hash_map2[num] > count:
result.extend([num] * count)
else:
result.extend([num] * hash_map2[num])
return result
202. 快乐数
解
def isHappy(n):
temp_res = 0
seen = set()
length = len(str(n))
while True:
# 每位平方相加得到temp_res
while length > 0:
m, n = divmod(n, 10**(length-1))
length -= 1
temp_res += m**2
# 等于1 即为快乐数
if temp_res == 1:
return True
# 记录出现过的数字
if temp_res not in seen:
n = temp_res
length = len(str(n))
seen.add(temp_res)
temp_res = 0
# 如果出现过则会导致死循环,则不是快乐数
else:
return False
1. 两数之和
解
def twoSum(nums, target):
# 获取每个值对应的下标hash
nums_hash = {}
for index, i in enumerate(nums):
if i not in nums_hash:
nums_hash[i] = [index]
else:
nums_hash[i].append(index)
for key, val in nums_hash.items():
# 查找差值是否存在
sub = target-key
if sub not in nums_hash:
continue
elif sub == key:
if len(val) == 1:
continue
else:
return val
else:
return [val[0], nums_hash[sub][0]]
更优解:
def two_sum(nums: List[int], target: int) -> List[int]:
hash_map = {}
for index, num in enumerate(nums):
sub = target - num
# 一开始hash_map是空的, 比如target = 6, nums = [2,3,4],遇到2时hash_map中没有6-4,则hash_map[2] = 0
# 继续向后遍历总会遇到 4,再查询hash_map时,2已经保存在其中,获取其下标即可。
# 因此不需要先整体遍历一次nums记录所有num-index映射
if sub in hash_map:
return [index, hash_map[sub]]
else:
hash_map[num] = index
454. 四数相加 II
解
# 时间复杂度:O(n^2)
# 空间复杂度: O(n)
def fourSumCount(A, B, C, D):
ans = 0
# 直接暴力计算的话时间复杂度要O(n^4),肯定会超时
# 这里采用分治,将4个列表分为两组,先计算AB两组两两元素和,及出现次数
AB_hash = {}
for index_a, a in enumerate(A):
for index_b, b in enumerate(B):
if (a + b) not in AB_hash:
AB_hash[a+b] = 1
else:
AB_hash[a+b] += 1
# 计算 0-c-d 是否在AB_hash中,如果在就存在解
for c in C:
for d in D:
if (-c-d) in AB_hash:
ans += AB_hash[-c-d]
return ans