今日任务:
1)哈希表理论基础
2)242.有效的字母异位词
3)349.两个数组的交集
4)202.快乐数
5)1.两数之和
242.有效的字母异位词
题目链接:242. 有效的字母异位词 - 力扣(LeetCode)
给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。
注意:若 s 和 t 中每个字符出现的次数都相同,则称 s 和 t 互为字母异位词。
示例 1:
输入: s = "anagram", t = "nagaram"
输出: true
示例 2:
输入: s = "rat", t = "car"
输出: false
文章讲解:代码随想录 (programmercarl.com)
视频讲解:学透哈希表,数组使用有技巧!Leetcode:242.有效的字母异位词哔哩哔哩bilibili
思路:
这题是要判断两个字符串中字符是否一样,包括字符出现的次数
立马想到用哈希表解决,将其中一个字符串用一个哈希表存放
具体用什么哈希表,我们可以用字典,或者数组
1)字典
遍历第一个字符串s,key为s中出现的字符,value为该字符出现的次数。
遍历第二个字符串t,判断每个字符是否出现在字典中,若出现,则相应的value - 1,若没有则直接返回False
最后遍历一边字典,当value出现了不为0的数,则返回False,顺利遍历完,则返回True
2)数组
这里用数组与上面一样,我们定义一个26个元素的数组,表示26个字母,0-25分别表示a-z,这里比较巧妙的的是,字母的ascii值是连续的,我们只需要用字母的ascii值减去a的ascii值,就能使0-15分别与a-z对应起来
class Solution:
def isAnagram(self, s: str, t: str) -> bool:
# todo 1定义一个数组,其下标对应26个字母,其值对应字符串中这个字母出现的次数
record = [0]*26
# todo 2统计s字符串中的字母出现次数
for i in s:
# 如果a表示0,a-z的ascii值连续,所以用相应的字母减去"a",即可得到a-z对应的0-26
record[ord(i)-ord('a')] += 1
# todo 3统计t字符串中字母出现的次数,每出现一个在s的基础上相应的位置-1
for i in t:
record[ord(i)-ord('a')] -= 1
# todo 4遍历数组,如果出现0意外的数,则表示这两个不是字母异位
for i in record:
if i !=0:
return False
return True
感想:
刚接触哈希表,对这个的使用还不是很熟练,看到一道题,不能立马联想到哈希表,还是题目做的不够多。这题还有一个比较巧妙的技巧,节省了内存空间,通常我们可能是将两个字符串转成哈希表,再比较,而这个只需转一个,另一个在上一个哈希表中做减法即可
时间复杂度:O(n)
空间复杂度:O(1)
349.两个数组的交集
题目链接:349. 两个数组的交集 - 力扣(LeetCode)
给定两个数组 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]
文章讲解:代码随想录 (programmercarl.com)
视频讲解:学透哈希表,set使用有技巧!Leetcode:349. 两个数组的交集哔哩哔哩bilibili
思路:
最简单的方法就是使用将两个列表转为集合,用&取两个集合交集(但这么做没什么意义)
我们换个思路
我们将其中一个数组专用哈希表存储,遍历另一个数组,判断里面的元素是否在哈希表中,如果在,则用集合保存(集合可以避免重复保存)
第一个数组转用哈希表存储,如果数组很大,那我们这里的哈希表可以选字典,如果这里的数组有限,不算大,也可以选数组做哈希表,数组哈希表只需比原数组的上限大一点点即可(以防万一)
方法一:使用字典和集合
class Solution:
# 使用字典和合集
def intersection(self, nums1: list[int], nums2: list[int]) -> list[int]:
# todo 1)将某个数组中所有元素用哈希表字典存储
table = {}
for n in nums1:
# table.get(n,0) 获取字典里key为n的值,如果没有该键,则返回0
table[n] = table.get(n, 0) + 1
# todo 2)遍历另一个数组,判断其是否在哈希表中,如果存在,则保存在集合中
result = set()
for n in nums2:
if n in table:
result.add(n)
del table[n] # 可加可不加,set本身就不重复保存
return list(result)
使用集合改进:
class Solution:
# 使用集合改进
def intersection(self, nums1: list[int], nums2: list[int]) -> list[int]:
return list(set(nums1) & set(nums2))
方法二:使用数组和集合
class Solution:
# 使用数组和合集
def intersection(self, nums1: list[int], nums2: list[int]) -> list[int]:
table = [0]*1005
for i in nums1:
table[i] += 1
result = set()
for i in nums2:
if table[i] != 0:
result.add(i)
return list(result)
感想:
这题可以转换成查找某个数在数组中是否存在,对于这样的问题我们可以毫不犹豫的选择哈希表
202.快乐数
编写一个算法来判断一个数 n 是不是快乐数。
「快乐数」 定义为:
对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。 然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。 如果这个过程 结果为 1,那么这个数就是快乐数。 如果 n 是 快乐数 就返回 true ;不是,则返回 false 。
示例 1:
输入:n = 19
输出:true
解释: 1^2 + 9^2 = 82
8^2 + 2^2 = 68
6^2 + 8^2 = 100
1^2 + 0^2 + 0^2 = 1
示例 2:
输入:n = 2
输出:false
文章讲解:代码随想录 (programmercarl.com)
思路:
这题有两个核心点
1)找到数的每一位
2)这题可能会无限循环下去,但我们不能让其无限循环下去,我们要判定什么时候开始了无线循环,并终止循环。其实出现无线循环的点,就是我们求的平方和开始出现了重复。
弄清楚这两点后面的就好做了
方法一:采用数学取余的方式得到该数的每一位,计算平方和并用集合收集
class Solution:
def isHappy(self, n: int) -> bool:
# todo 2.将平方和用set收集起来,一段出现重复则返回false,或当平方和为1时返回true
res = set()
while True:
n = self.getSum(n) # 注意这里不能新定义变量,这里是将计算的和作为下一次的数继续求平方和
if n == 1:
return True
# 如果中间结果重复出现,说明陷入死循环了,该数不是快乐数
if n not in res:
res.add(n)
else:
return False
# todo 1.采用数学取余的方式得到数的每一位,并计算平方和
def getSum(self, n: int) -> int:
sqraSum = 0
# i = 0
while n:
n, r = divmod(n, 10)
sqraSum += r ** 2
return sqraSum
方法二:将数字转换为字符串,遍历得到该数的每一位,计算平方和并用集合收集
class Solution:
def isHappy(self, n: int) -> bool:
res = set()
while n not in res: # 判断n是否在集合中,不在则添加到集合中,集中有没有初始n本身并不影响
res.add(n)
# 将数字 n 转换成字符串
n_str = str(n)
sum = 0
for i in n_str:
sum += int(i) ** 2
if sum == 1:
return True
else:
n = sum
return False
感想:
这题在第一个方法中,出现了问题,在n = self.getSum(n) 中我定义了一个新变量去赋值,导致第二次还是计算的n一下就退出循环了,debug每一个变量才发现问题所在。这完全是粗心导致的,找问题花费了大量时间。
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]
文章讲解:代码随想录 (programmercarl.com)
视频讲解:梦开始的地方,Leetcode:1.两数之和,学透哈希表,map使用有技巧!哔哩哔哩bilibili
思路:
最简单的方法,暴力法,for循环遍历数组,找出该指针之后是否有值与当前指针相加为target值,有则返回
还有一个方法就是采用哈希表改进,这里选用字典作为哈希表,采用字典的key保存数组的元素,而value保存数组的下标,遍历数组,直接查找字典中是否存在与当前指针的差值,存在返回下标即可
方法一:暴力法
class Solution:
# 暴力法,两层循环
def twoSum(self, nums: list[int], target: int):
for i in range(len(nums)):
div = target - nums[i]
for j in range(i+1,len(nums)):
if nums[j] == div:
return [i,j]
方法二:采用哈希表(字典)改进
class Solution:
# 方法二:采用哈希表(字典)改进
def twoSum(self, nums: list[int], target: int):
res = dict()
for i,num in enumerate(nums):
div = target - num
if div in res:
return [res[div],i]
res[num] = i
return []
感想:
在方法二中,我曾陷入了一个坑,我最初的想法是直接把一个数组转为字典哈希表存储,但在写代码过程中,我发了问题,key是不能重复的,而数组中的数是可能重复的,我也不能直接去重(原因见案例示例3),但我必须要把数组元素存为key才能更方便查找,到这里思路就卡了。
而上面代码很巧妙的解决了这个问题,我们将数组转为字典的过程中,同时把其他的部分也判断了。遍历过程中,核心是将已经遍历过的元素添加到哈希表中,同时,在已经遍历过的元素中寻找是否有当前指针的差值,这样完美解决了我刚刚说的问题(相同的数相加得到目标值,这里相同数并不是同一个位置的元素,只是值相同),以及规避了自己不能与自己想加的问题(我是在遍历过的数中找差值,没有包含档前指针,所以不存在自己与自己相加的问题)
本文介绍了如何使用哈希表解决LeetCode中的四个问题:判断字母异位词、寻找两个数组交集、检查快乐数和寻找两数之和。通过实例演示了字典、数组和集合在这些问题中的应用,以及优化空间复杂度的方法。
1129

被折叠的 条评论
为什么被折叠?



