什么时候使用哈希法,当我们需要查询一个元素是否出现过,或者一个元素是否在集合里的时候,就要第一时间想到哈希法。
-
242.有效的字母异位词
# 给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的 字母异位词。
# 示例 1:
# 输入: s = "anagram", t = "nagaram"
# 输出: true
# 示例 2:
# 输入: s = "rat", t = "car"
# 输出: false
# 提示:
#
# 1 <= s.length, t.length <= 5 * 10⁴
# s 和 t 仅包含小写字母
class Solution:
def isAnagram(self, s: str, t: str) -> bool:
record = [0] * 26
for i in s:
record[ord(i) - ord("a")] += 1
for i in t:
record[ord(i) - ord("a")] -= 1
for i in range(26):
if record[i] != 0:
return False
return True
ord(i) - ord("a")
是把字符串中的小写字母映射为 0 - 25 范围内的整数,进而当作列表 record
的索引;
ord()
函数
ord()
是 Python 的内置函数,其功能是返回一个字符对应的 Unicode 码点(整数)。比如,ord('a')
返回 97,ord('b')
返回 98,依此类推,ord('z')
返回 122。
ord(i) - ord("a")
的作用
由于题目限定字符串 s
和 t
仅包含小写字母,小写字母 a - z
的 Unicode 码点是连续的。ord("a")
是小写字母 a
的 Unicode 码点(值为 97),ord(i)
是当前字符 i
的 Unicode 码点。用 ord(i) - ord("a")
就能得到当前字符 i
相对于字母 a
的偏移量。
前两个for循环,分别记录字符串s、t中每个字符出现的索引位置和出现次数。
第一个for循环在出现次数+1,第二个for循环在出现次数-1.
然后第三个for循环检查整个记录的列表,如果有某个索引位置的出现次数不为 0,就说明字符串 s
和 t
中该字母出现的次数不一样,也就意味着它们不是字母异位词,此时返回 False;反之True。
-
349. 两个数组的交集
使用字典和集合
349. 两个数组的交集
给定两个数组 nums1 和 nums2 ,返回 它们的 交集 。输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序 。
示例 1:
输入:nums1 = [1,2,2,1], nums2 = [2,2]
输出:[2]
示例 2:
输入:nums1 = [4,9,5], nums2 = [9,4,9,8,4]
输出:[9,4]
解释:[4,9] 也是可通过的
class Solution:
def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
table = {}
for num in nums1:
table[num] = table.get(num, 0) + 1
res = set()
for num in nums2:
if num in table:
res.add(num)
# del table[num]
return list(res)
这题改用set,而不继续用数组,是因为:
使用数组来做哈希的题目,是因为题目都限制了数值的大小。
而这道题目没有限制数值的大小,就无法使用数组来做哈希表了。
而且如果哈希值比较少、特别分散、跨度非常大,使用数组就造成空间的极大浪费。
此时就要使用另一种结构体了,set 。
第一个for循环:遍历nums1,将元素作为键存入哈希表,值为该元素出现的次数
其中
table[num] = table.get(num, 0) + 1
是获取字典 table 中键为 num 的值,如果键 num 不存在则返回默认值 0,然后将这个值加 1。这样做通常是为了统计 num 在某个序列中出现的次数,每次遇到 num 就将其对应的值加 1
第一个for循环:在nums2中检查每个字符是否在哈希表table中,若存在则为交集,将其加到res中。并且从table中删除该字符,避免后面重复添加。(注意添加和删除的是两个对象,res和table。)
但是由于集合set是会自动去重的,所以del table[num]只是减小了代码计算量。
拓展
那有同学可能问了,遇到哈希问题我直接都用set不就得了,用什么数组啊。
直接使用set 不仅占用空间比数组大,而且速度要比数组慢,set把数值映射到key上都要做hash计算的。
不要小瞧 这个耗时,在数据量大的情况,差距是很明显的。
-
202. 快乐数
给定两个数组 nums1
和 nums2
,返回 它们的 交集 。输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序 。
示例 1:
输入:nums1 = [1,2,2,1], nums2 = [2,2] 输出:[2]
示例 2:
输入:nums1 = [4,9,5], nums2 = [9,4,9,8,4] 输出:[9,4] 解释:[4,9] 也是可通过的
class Solution:
def isHappy(self, n: int) -> bool:
record = set()
while True:
n = self.get_sum(n)
if n == 1:
return True
# 如果中间结果重复出现,说明陷入死循环了,该数不是快乐数
if n in record:
return False
else:
record.add(n)
def get_sum(self,n: int) -> int:
new_num = 0
while n:
n, r = divmod(n, 10)
new_num += r ** 2
return new_num
通过定义get_sum,来计算一个数每个位置上的数字的平方和。
divmod(n, 10)是 Python 中的一个内置函数,它接收两个参数,这里接收的参数是整数 n 和整数 10。这个函数的作用是将整数 n 除以 10,返回一个包含商和余数的元组。例如,如果 n 是 123,那么 divmod(n, 10)将返回(12, 3),其中 12 是商,3 是余数。
即使用 divmod 函数同时获取商和余数。循环中得到的商也在不断变化。
通过for循环,通过除10取余得到个、十、百等位数。然后将每一个位数的平方加到new_num。
-
1. 两数之和
给定一个整数数组 nums
和一个整数目标值 target
,请你在该数组中找出 和为目标值 target
的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案,并且你不能使用两次相同的元素。
你可以按任意顺序返回答案。
示例 1:
输入:nums = [2,7,11,15], target = 9 输出:[0,1] 解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。
示例 2:
输入:nums = [3,2,4], target = 6 输出:[1,2]
示例 3:
输入:nums = [3,3], target = 6 输出:[0,1]
因为本题,我们不仅要知道元素有没有遍历过,还要知道这个元素对应的下标,需要使用 key value结构来存放,key来存元素,value来存下标,那么使用map正合适。
再来看一下使用数组和set来做哈希法的局限。
- 数组的大小是受限制的,而且如果元素很少,而哈希值太大会造成内存空间的浪费。
- set是一个集合,里面放的元素只能是一个key,而两数之和这道题目,不仅要判断y是否存在而且还要记录y的下标位置,因为要返回x 和 y的下标。所以set 也不能用。
c++的map,在python中与之对应的数据结构是字典dict()。
法1,用字典:
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
# 初始化一个空字典,用于存储元素及其索引
records = dict()
# 遍历数组,同时获取元素的索引和值
for index, value in enumerate(nums):
# 检查 target - 当前值 是否在字典中
if target - value in records: # 遍历当前元素,并在map中寻找是否有匹配的key
# 如果存在,说明找到了两个数相加等于 target,返回它们的索引
return [records[target - value], index]
# 如果没找到匹配对,就把当前元素和它的下标加入到字典中
records[value] = index # 如果没找到匹配对,就把访问过的元素和下标加入到map中
# 如果遍历完整个数组都没有找到符合条件的两个数,返回空列表
return []
enumerate(nums)同时获取元素的索引和值
法2,暴力:
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
for i in range(len(nums)):
for j in range(i+1, len(nums)):
if nums[i] + nums[j] == target:
return [i,j]