哈希表Part01
242.有效的字母异位词
题目描述:leetcode|242.有效的字母异位词
文档讲解:代码随想录
视频讲解:学透哈希表,数组使用有技巧!Leetcode:242.有效的字母异位词
状态:初刷
题目描述
给定两个字符串 s
和 t
,编写一个函数来判断 t
是否是 s
的 字母异位词。
示例 1:
输入: s = “anagram”, t = “nagaram”
输出: true
示例 2:
输入: s = “rat”, t = “car”
输出: false
提示:
1 <= s.length, t.length <= 5 * 104
s
和 t
仅包含小写字母
进阶: 如果输入字符串包含 unicode
字符怎么办?你能否调整你的解法来应对这种情况?
代码实现
什么是有效字母异位词:
字母异位词:是由重新排列源单词的所有字母得到的一个新单词。
有效字母异位词:给定两个字符串,判定这两个字符串,是不是由相同的字母组成,但是字母的位置可以不一样。
方法一:map
注意:双层循环和双循环是不一样的。
def isAnagram(s: str, t: str) -> bool:
# 方式1 使用map
from collections import defaultdict
# 定义两个字典
dic_s, dic_t = defaultdict(int), defaultdict(int)
for i in s:
dic_s[i] += 1
for i in t:
dic_t[i] += 1
return dic_s == dic_t
方法二:数组
def isAnagram(s: str, t: str) -> bool:
# 使用数组,因为都是小写英文字母,那么我们定义一个维度为26的数组,出现某个字母,就在这个字母索引/位置上+=1
record = [0]*26
for i in s:
index = ord(i) - ord('a')
record[index] += 1
for i in t:
index = ord(i) - ord('a')
record[index] -= 1
for i in range(26):
if record[i] != 0:
return False
return True
方法三:Counter
def isAnagram(s: str, t: str) -> bool:
# 方式3,使用counter
from collections import Counter
s_count = Counter(s)
t_count = Counter(t)
return s_count == t_count
知识点
哈希表
哈希表定义:
-
根据关键码的值而直接进行访问的数据结构。
-
哈希表中关键码就是数组的索引下标,通过下标直接访问数组中的元素。
哈希表的用法:
- 一般都是用来 快速判断一个元素是否出现在集合里。
特点:
- 牺牲了空间换取时间。
哈希表的三种数据结构:
- 数组、
set
(集合)、map
(映射)- 范围可控用数组
- 数值很大就用
set
- 如果
k
对应的value
的话就用map
Defaultdict
- 普通字典
dict
不能自动创建未定义的键值对。
是内置类型dict
的一个实例。键必须存在才能访问对应的值,否则会抛出keyerror
。
record = dict()
result = {}
record['a'] = 1
print(record['a']) # 输出: 1
print(record['b']) # KeyError: 'b'
需要手动检查和初始化
- 使用
defaultdict(list)
创建的字典可以自动创建未定义的键值对。
使用了collections.defaultdict
,一个子类化的字典,可以在访问不存在的键时自动创建默认值。
提供了一个工厂函数(比如list
,int
,set
等),当访问一个不存在的键时,自动调用这个函数生成默认值并插入字典中,
可以避免keyerror
,适合用来分组、聚合等操作
from collections import defaultdict
result = defaultdict(list)
dic_t = defaultdict(int)
result['a'].append(1)
result['b'].append(2)
print(result['a']) # 输出: [1]
print(result['c']) # 输出: [],不会报错,因为自动创建了一个空列表
defaultdict(int) # 默认是 0
defaultdict(set) # 默认是空集合
defaultdict(str) # 默认是空字符串
需要自动处理缺失键的情况(例如进行列表追加、计数、分组等)
Counter
collections.Counter
是 Python
标准库中 collections
模块的一个非常实用的类,用于统计可迭代对象中元素的出现次数。它本质上是一个字典子类(dict subclass
),其中键是元素,值是该元素出现的次数。
from collections import Counter
fruits = ['apple', 'banana', 'apple', 'orange', 'banana', 'apple']
counter = Counter(fruits)
print(counter)
# 输出: Counter({'apple': 3, 'banana': 2, 'orange': 1})
.elements()
返回一个迭代器,按计数展开所有元素(忽略计数为0
或负的)
c = Counter(a=3, b=1)
list(c.elements()) # 输出: ['a', 'a', 'a', 'b']
.most_common([n])
返回最常见的n
个元素及计数(默认从高到低排序)
c = Counter('abracadabra').most_common(3)
print(c) # [('a', 5), ('b', 2), ('r', 2)]
.subtract([iterable-or-mapping])
从另一个可迭代对象或映射中减去计数(可以是负数)
c = Counter(a=4, b=2)
c.subtract({'a': 1, 'b': 3})
print(c) # Counter({'a': 3, 'b': -1})
- 数学运算(支持
+
、-
、&
、|
等)
+
: 合并两个Counter
,对应项相加(只保留正数)-
: 减法,只保留正的结果&
: 取交集,取最小计数|
: 取并集,取最大计数
c1 = Counter(a=3, b=1)
c2 = Counter(a=1, b=2)
print(c1 + c2) # Counter({'a': 4, 'b': 3})
print(c1 - c2) # Counter({'a': 2})
print(c1 & c2) # Counter({'a': 1, 'b': 1})
print(c1 | c2) # Counter({'a': 3, 'b': 2})
Counter
默认不存储计数为0
或负数的元素。- 如果你想清除计数为
0
的项,可以用.subtract()
后加上.+
(即+Counter()
):
c = Counter(a=2, b=-1)
c = +c
print(c) # Counter({'a': 2})
ord()
- 用于获取字符的
Unicode
码点(数值)。适合处理字符串、字符编码、加密算法等场景中。 - 只接收长度为
1
的字符(字符串类型)。 - 返回该字符对应的
Unicode
编码(整数)。
print(ord('a')) # 输出: 97
print(ord('A')) # 输出: 65
print(ord('0')) # 输出: 48
print(ord('我')) # 输出: 25105(中文字符的 Unicode)
print(ord('\n')) # 输出: 10(换行符)
349. 两个数组的交集
题目描述:leetcode|349. 两个数组的交集
文档讲解:代码随想录
视频讲解:学透哈希表,set使用有技巧!Leetcode: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] 也是可通过的
提示:
1 <= nums1.length, nums2.length <= 1000
0 <= nums1[i], nums2[i] <= 1000
代码实现
输出结果中的每一个元素一定是唯一的,也就是 输出的结果是去重的,同时可以不考虑输出结果的顺序。
但是这道题没有限制数组的大小,因此无法使用数组做哈希表。
方法一:使用字典和列表/集合
def intersection(nums1, nums2):
# 先构建一个空字典存储出现在第一个数组中的元素及其出现的次数
table = {}
for i in nums1:
table[i] = table.get(i, 0) + 1
# 因为结果不重复,所以使用一个集合存储结果
result = set() # result = []
# 遍历第二个数组
for i in nums2:
if i in table:
result.add(i) # result.append(i)
del table[i]
return list(result)
方法二:使用数组
def intersection(nums1, nums2):
# 方法二,使用数组,最后将两个结果集进行相乘,如果大于0 则添加进去
result = []
# 使用数组
nums_1 = [0] * 1001
nums_2 = [0] * 1001
for num1 in nums1:
nums_1[num1] += 1
for num2 in nums2:
nums_2[num2] += 1
for i in range(1001):
if nums_1[i] * nums_2[i] > 0:
result.append(i)
return result
方式三:只使用集合
def intersection(nums1, nums2):
# 方式三:只使用集合
set_nums1 = set(nums1)
set_nums2 = set(nums2)
return list(set_nums1 & set_nums2)
知识点
1. 什么时候用数组,什么时候用map
数组:使用数组来做哈希的题目,是因为题目都限制了数值的大小。
- 如果哈希值比较少、特别分散、跨度非常大,使用数组就造成空间的极大浪费。
set
:不仅占用空间比数组大,而且速度慢,set
把数值映射到Key
上都要做hash
计算。对于数据量大的情况,差距比较明显。
2. set
集合的相关运算方式
- 添加
a = set()
a.add([1,2])
#只能是数字、字符串、元组或布尔类型,不能添加可变的数据.
- 删除
a = {1,2,3}
a.remove(1)
# 要确保删除的元素包含在集合中。
a = {1,2,3}
a.discard()
# 即使删除的元素不在集合中也不会报错。
- 交集
set1 & set2
- 并集
set1|set2
- 差集
set1-set2
- 对称差集
set1^set2
202. 快乐数
题目描述:leetcode|202. 快乐数
文档讲解:代码随想录
状态:有点费劲
题目描述
编写一个算法来判断一个数 n
是不是快乐数。
「快乐数」 定义为:
- 对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。
- 然后重复这个过程直到这个数变为
1
,也可能是 无限循环 但始终变不到1
。 - 如果这个过程 结果为
1
,那么这个数就是快乐数。 - 如果
n
是 快乐数 就返回true
;不是,则返回false
。
示例 1:
输入:n = 19
输出:true
解释:
12 + 92 = 82
82 + 22 = 68
62 + 82 = 100
12 + 02 + 02 = 1
示例 2:
输入:n = 2
输出:false
提示:
1 <= n <= 231 - 1
代码实现
如果得到的数据和曾经出现过,那么说明会陷入死循环,因此把每次得到的新的结果存到一个集合中,每次计算得到新的值时都与原有的集合进行比较。
- 如果新的数据和曾经在原数据和集合中出现过,那么必然会陷入死循环
- 如果新的数据和为
1
那么是快乐数
def getsum(n):
return sum(int(i)**2 for i in str(n))
def isHappy(n: int) -> bool:
seen = []
while n != 1:
if n in seen:
return False
seen.append(n)
n = getsum(n)
def getsum(n):
num_sum = 0
while n:
n,r = divmod(n,10)
num_sum += r**2
return num_sum
def isHappy(n: int) -> bool:
result = set()
while n not in result:
result.add(n)
numsum = getsum(n)
if numsum == 1:
return True
n = numsum
return False
知识点
给定一个整数 求 这个数 每个位置上的数字的平方和
方法一:使用sum
def getsum(n):
return sum(int(i)**2 for i in str(n))
方法二:遍历元素
def getsum(n):
num_sum = 0
for i in str(n):
num_sum += int(i)**2
return num_sum
方法三:使用divmod
def getsum(n):
num_sum = 0
while n:
n,r = divmod(n,10)
num_sum += r**2
return num_sum
divmod()
是 Python
的内置函数,用于同时返回除法和取余的结果。
它接受两个参数,表示被除数和除数,返回一个包含两个值的元组,第一个值表示相除的商,第二个值表示相除的余数
num1 = 10
num2 = 3
result = divmod(num1, num2)
print(result) # 输出: (3, 1)
1. 两数之和
题目描述:leetcode|1. 两数之和
文档讲解:代码随想录
视频讲解:梦开始的地方,Leetcode:1.两数之和,学透哈希表,map使用有技巧!
状态:n刷
题目描述
给定一个整数数组 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]
提示:
2 <= nums.length <= 104
-109 <= nums[i] <= 109
-109 <= target <= 109
- 只会存在一个有效答案
代码实现
创建一个哈希表,对于每一个 x
,我们首先查询哈希表中是否存在 target - x
,
然后将 x
插入到哈希表中,即可保证不会让 x
和自己匹配。
def twosum(nums, target):
n = len(nums)
other = {}
if n < 2:
return []
for index in range(n):
diff = target - nums[index]
if diff in other:
return [other[diff], index]
other[nums[index]] = index
return []
def twosum(nums, target):
n = len(nums)
other = {}
if n < 2:
return []
for index in range(n):
diff = target - nums[index]
if nums[index] in other:
return [other[nums[index]], index]
other[diff] = index
return []