文章链接:代码随想录
哈希表理论基础 + 242.有效的字母异位词 + 349. 两个数组的交集 + 202.快乐数 + 1. 两数之和
视频链接:学透哈希表,数组使用有技巧!Leetcode:242.有效的字母异位词_哔哩哔哩_bilibili
实际写代码过程中已经多次用过哈希表思想了,但没有过系统学习,在此重新学习一遍。
引自代码随想录:当我们遇到了要快速判断一个元素是否出现集合里的时候,就要考虑哈希法。
242.有效的字母异位词
状态:先看视频,学习解题思路,再自己复现代码
思路:用哈希表的数组结构,做减法:
1)统计第一个字符串s的字母出现情况:26个小写字母ASCII码与字母 'a' ASCII码的差值对应hashlist各元素的下标,下标范围[0, 25]
2)在统计好的hashlist上做减法:字符串t每出现一次与s相同的字母,hashlist中对应的元素减1
3)若最终的hashlist为全0,则s 和 t是有效的字母异位词
总结:
1. 使用哈希表时,常用的3种数据结构有:
1)数组:适用于数据范围可控(或较小)的情况
2)集合set:相比数组,更适合于数组范围较大、较分散的情况
3) 映射map:适用于哈希值特别分散、跨度大的情况
2. Python中,要用ord()函数来获取对应的 ASCII 数值,不支持字符直接相减
3. 时间复杂度: O(n)
空间复杂度: O(1)
class Solution:
def isAnagram(self, s: str, t: str) -> bool:
hashlist = [0] * 26
for i in s:
hashlist[ord(i) - ord('a')] += 1
for i in t:
hashlist[ord(i) - ord('a')] -= 1
for i in range(len(hashlist)):
if hashlist[i] != 0:
return False
return True
349. 两个数组的交集
状态:直接使用python中set()的自带功能,一行搞定;但还可以用数组方式解决
思路:看过视频和文章讲解,除了上述两种方法,还有字典+集合方法
1)方法一:直接使用python中set()的自带功能找交集,set() & set()或set().intersection(set())
2)方法二:使用数组,两个for循环分别记录两个nums的元素出现次数,再对比是否有交集
3)方法三:使用字典+集合方法,先用dict存第一个数组的所有元素,再用一个for循环遍历第二个数组,查看其元素是否存在于dict中,同时用set存储交集。注意:set存过的元素要从dict()中删除,减少重复操作
总结:
1. Python的set()是无序的不重复元素序列,自带功能挺强大的,可以合理利用
2. 方法二对比是否有交集可以用不同方式:if count1[k] and count2[k] 或者 if count1[k] * count2[k] > 0 均可
# set()自带功能
class Solution:
def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
# return list(set(nums1) & set(nums2))
return list(set(nums1).intersection(set(nums2)))
# 方法二:使用数组
class Solution:
def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
count1, count2 = [0]*1001, [0]*1001
result = []
for i in nums1:
count1[i] += 1
for i in nums2:
count2[i] += 1
for k in range(len(count1)):
if count1[k] and count2[k]: # 也可用 count1[k] * count2[k] > 0
result.append(k)
return result
202.快乐数
状态:先看文章讲解和示例代码,再自己复现,但过程中有问题出现
思路:
抓住题目中的重点:题干说了会 无限循环,也就是说求和的过程中,sum会重复出现,即判断一个元素是否出现在集合里;求和过程就是数值不断对10取模得余数再求平方和
总结:
1. 第一次看题,没有理解“无限循环”, 看过题解后才理解为什么要用哈希法
2. 看过python示例代码,但是复现过程中发现自己还是忽略了一些逻辑细节问题:解法一里的传入参数和判断变量必须保持一致(均为n),不能用新变量替换(错误写法放在下面)。因为n是不断更新的,需要同时作为判断条件和循环语遍历的范围来使用
3. 另一种解法,使用集合,并且使用str(n)将int型数值转换为字符串,方便进行求和操作,和第一个解法相比更简洁快速,但是和代码随想录的版本二写法相比还是稍显复杂
4. 时间复杂度: O(logn)
空间复杂度: O(logn)
错误代码:
class Solution:
def isHappy(self, n: int) -> bool:
record = set()
while True:
# 不能定义新变量sums, 而应与传入参数n保持一致,
# 否则在下一次循环中,求和时本应传入此刻的sums值,却依然传入的是n
# 如果要写为sums = self.get_sum(sums),则函数isHappy()的传入参数应改为sum:int
sums = self.get_sum(n)
if sums == 1:
return True
if sums in record:
return False
else:
record.add(sums)
def get_sum(self, num:int) -> int: # 传入参数应为n
new_sum = 0
while num: # 死循环,传入参数num一直不变
n, r = divmod(num, 10) # 同样的,num要改为n
new_sum += r **2
return new_sum
正确代码:
# 解法一(与错误代码作对比)
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_sum = 0
while n:
n, r = divmod(n, 10)
new_sum += r **2
return new_sum
# 解法二
class Solution:
def isHappy(self, n: int) -> bool:
record = set()
while True:
new_sum = 0
n_str = str(n)
for i in n_str:
new_sum += int(i) ** 2
n = new_sum
if n == 1:
return True
if n in record:
return False
else:
record.add(n)
1. 两数之和
状态:先看视频,再自己写代码
思路:
1. 暴力解法:两个for循环,两两元素依次相加,直到找到和等于target的两个元素
2. 哈希法:使用一个dict()存储遍历过的元素(同时存数值和下标),在遍历数组的同时,从dict()中查找是否存在某元素等于(target - 当前所指向的元素)
总结:
1. 理解4个重点问题:
- 为什么会想到用哈希表
- 按照思路2,要在已存储的集合中查找是否存在符合条件的某元素,考虑用哈希法
- 哈希表为什么用map
- 题目要求返回元素对应的下标。在查找元素时,首先要利用元素的数值判断是否符合条件,如符合条件,则需要返回其下标,所以元素的数值value和下标key要同时存储在集合中
- 本题map是用来存什么的
- 存储已遍历过的元素
- map中的key和value用来存什么的
- key对应元素的数值,value对应元素的下标
2. Python 中的dict() 与C++的map()相似,dict()的功能也很强大,要合理利用
3. Python 中的enumerate()函数将一个可迭代对象(如列表、元组、字符串等)组合为一个索引序列,返回的结果是一个元组,包含索引和对应的元素。这个函数常用于循环迭代时,需要同时获取元素和其对应的索引。
4. 哈希法 时间复杂度: O(n), 空间复杂度: O(n)
# 暴力解法
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]
# 哈希法
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
record = dict()
for k, v in enumerate(nums):
if target - v in record:
return [record[target - v], k]
record[v] = k
return []