代码随想录算法训练营Day5 | Leetcode 242有效的字母异位词 、349两个数组的交集、202快乐数、1两数之和
一、有效的字母异位词
相关题目:Leetcode242
文档讲解:Leetcode242
视频讲解:B站代码随想录
1. Leetcode242.有效的字母异位词
给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。
示例 1: 输入: s = “anagram”, t = “nagaram” 输出: true
示例 2: 输入: s = “rat”, t = “car” 输出: false
说明: 你可以假设字符串只包含小写字母
-
思路:
- 定义数组 record 记录字符串 s 里字符出现的次数 a,把字符映射到数组(哈希表)的索引下标上,因为字符 a 到字符 z 的 ASCII 是26个连续的数值,所以字符 a 映射到下标0上,相应的字符 z 映射到下标25上。
- 遍历字符串 s ,将 s[i] - ‘a’ 所在的元素做 +1 操作;遍历字符串 t ,对 t 中出现的字符映射哈希表索引上的数值再做 -1 的操作。
- 最后检查 record 数组如果有的元素不为0,则 return false;如果 record 数组所有元素都为0,说明字符串 s 和 t 是字母异位词,return true。
-
注意:
- 当遇到了要快速判断一个元素是否出现集合里的时候,就要考虑哈希法。
- 数组就是简单的哈希表,但是数组的大小可不是无限开辟的。
-
哈希法
class Solution:
def isAnagram(self, s: str, t: str) -> bool:
record = [0] * 26
for i in s:
#并不需要记住字符a的ASCII,只要求出一个相对数值就可以了
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:
#record数组如果有的元素不为零0,说明字符串s和t 一定是谁多了字符或者谁少了字符。
return False
return True
- 非哈希(内置函数)
#利用defaultdict
class Solution:
def isAnagram(self, s: str, t: str) -> bool:
from collections import defaultdict
s_dict = defaultdict(int)
t_dict = defaultdict(int)
for x in s:
s_dict[x] += 1
for x in t:
t_dict[x] += 1
return s_dict == t_dict
#利用Counter
class Solution(object):
def isAnagram(self, s: str, t: str) -> bool:
from collections import Counter
a_count = Counter(s)
b_count = Counter(t)
return a_count == b_count
二、两个数组的交集
相关题目:Leetcode349
文档讲解:Leetcode349
视频讲解:B站代码随想录
1. Leetcode349.有效的字母异位词
给定两个数组 nums1 和 nums2 ,返回它们的 交集 。输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序 。
提示:
1 <= nums1.length, nums2.length <= 1000
0 <= nums1[i], nums2[i] <= 1000
-
思路:
- 使用字典与集合:利用字典记录一个数组的值,遍历另一个数组查找共同元素,以集合储存结果(确保元素不重复)。
- 使用数组:定义两个长为1001的数组分别遍历 nums1 和 nums2 进行计数,定义结果数组并遍历计数数组,储存两个计数数组中皆非零的元素。
-
注意:
- 使用数组来做哈希的题目,是因为题目都限制了数值的大小,若这道题目没有限制数值的大小,就无法使用数组来做哈希表了。
- 如果哈希值比较少、特别分散、跨度非常大,使用数组就造成空间的极大浪费。
- 直接使用 set 不仅占用空间比数组大,而且速度要比数组慢,set 把数值映射到 key 上需要做 hash 计算的。在数据量大的情况,差距很明显。
-
使用字典和集合
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)
- 使用数组
class Solution:
def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
count1 = [0]*1001
count2 = [0]*1001
result = []
for i in range(len(nums1)):
count1[nums1[i]]+=1
for j in range(len(nums2)):
count2[nums2[j]]+=1
for k in range(1001):
if count1[k]*count2[k]>0:
result.append(k)
return result
- 使用集合
class Solution:
def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
return list(set(nums1) & set(nums2))
三、快乐数
相关题目:Leetcode202
文档讲解:Leetcode202
1. Leetcode202.快乐数
编写一个算法来判断一个数 n 是不是快乐数。
「快乐数」 定义为:对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。如果这个过程 结果为 1,那么这个数就是快乐数。
如果 n 是 快乐数 就返回 true ;不是,则返回 false 。
-
思路:
- 题目中的 无限循环 指的是求和的过程中,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_num = 0
while n:
n, r = divmod(n, 10)
new_num += r ** 2
return new_num
#版本二
class Solution:
def isHappy(self, n: int) -> bool:
record = set()
while n not in record:
record.add(n)
new_num = 0
n_str = str(n)
for i in n_str:
new_num+=int(i)**2
if new_num==1: return True
else: n = new_num
return False
#版本二精简
class Solution:
def isHappy(self, n: int) -> bool:
seen = set()
while n != 1:
n = sum(int(i) ** 2 for i in str(n))
if n in seen:
return False
seen.add(n)
return True
- 使用数组
#使用数组
class Solution:
def isHappy(self, n: int) -> bool:
record = []
while n not in record:
record.append(n)
new_num = 0
n_str = str(n)
for i in n_str:
new_num+=int(i)**2
if new_num==1: return True
else: n = new_num
return False
#使用数组精简
class Solution:
def isHappy(self, n: int) -> bool:
seen = []
while n != 1:
n = sum(int(i) ** 2 for i in str(n))
if n in seen:
return False
seen.append(n)
return True
- 快慢指针法
class Solution:
def isHappy(self, n: int) -> bool:
slow = n
fast = n
while self.get_sum(fast) != 1 and self.get_sum(self.get_sum(fast)):
slow = self.get_sum(slow)
fast = self.get_sum(self.get_sum(fast))
if slow == fast:
return False
return True
def get_sum(self,n: int) -> int:
new_num = 0
while n:
n, r = divmod(n, 10)
new_num += r ** 2
return new_num
四、两数之和
1. Leetcode1.两数之和
给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案,并且你不能使用两次相同的元素。
你可以按任意顺序返回答案。
-
思路:
- 为什么会想到用哈希表:本题不仅要知道元素有没有遍历过,还要知道这个元素对应的下标,需要使用 key value 结构来存放,key来存元素,value来存下标,使用 dict 正合适。
- 哈希表为什么用 dict:
- 数组的大小是受限制的,而且如果元素很少,而哈希值太大会造成内存空间的浪费。
- set 是一个集合,里面放的元素只能是一个 key,本题需要判断元素是否存在而且还要记录元素的下标位置。
- 本题 dict 是用来存什么的:dict 目的用来存放我们访问过的元素,因为遍历数组的时候,需要记录我们之前遍历过哪些元素和对应的下标,这样才能找到与当前元素相匹配的元素及下标。
- dict 中的 key 和 value 用来存什么的:我们需要给出一个元素,判断这个元素是否出现过,如果出现过,返回这个元素的下标。那么判断元素是否出现,这个元素就要作为 key,所以数组中的元素作为 key,有 key 对应的就是 value,value 用来存下标。
-
使用字典
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
records = dict()
for index, value in enumerate(nums):
if target - value in records: # 遍历当前元素,并在map中寻找是否有匹配的key
return [records[target- value], index]
records[value] = index # 如果没找到匹配对,就把访问过的元素和下标加入到map中
return []
- 使用集合
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
#创建一个集合来存储我们目前看到的数字
seen = set()
for i, num in enumerate(nums):
complement = target - num
if complement in seen:
return [nums.index(complement), i]
seen.add(num)
- 双指针法
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
# 对输入列表进行排序
nums_sorted = sorted(nums)
# 使用双指针
left = 0
right = len(nums_sorted) - 1
while left < right:
current_sum = nums_sorted[left] + nums_sorted[right]
if current_sum == target:
# 如果和等于目标数,则返回两个数的下标
left_index = nums.index(nums_sorted[left])
right_index = nums.index(nums_sorted[right])
if left_index == right_index:
right_index = nums[left_index+1:].index(nums_sorted[right]) + left_index + 1
return [left_index, right_index]
elif current_sum < target:
# 如果总和小于目标,则将左侧指针向右移动
left += 1
else:
# 如果总和大于目标值,则将右指针向左移动
right -= 1
- 暴力法
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]