文章链接:代码随想录
454.四数相加II + 383. 赎金信 + 15. 三数之和 + 18. 四数之和
视频链接:学透哈希表,map使用有技巧!LeetCode:454.四数相加II_哔哩哔哩_bilibili
哈希表第二部分,进一步体会哈希表的适用场景,双指针的使用、以及在三数之和、四数之和的去重和剪纸问题。
454.四数相加II
状态:在学过leetcode 1 两数之和的基础上,对这道题有一定的思路:考虑到了先计算前两个数组的a+b,再从后两个数组中找符合条件的元素;看视频确认思路是否正确,再自己写代码
思路:用dict()记录前两个数组的a+b之和及其出现的次数;遍历后两个数组,从dict()中找到等于0-(c+d)的key,并将相应的value(出现次数)加到count中
总结:
1. 思路比较清晰,但最开始没有想清楚怎样计算元组个数的问题, 题目不要求去重,所以直接统计dict()中key对应的value
2. 遍历前两个数组,dict()初始化为空字典 {} ,进行dict()的键值对更新时,用的是if判断语句:如果key存在,则更新sum[key] += 1; 否则,添加键值对sum[key] = 1
3. 时间复杂度 O(n^2), 空间复杂度 O(n^2)
class Solution:
def fourSumCount(self, nums1: List[int], nums2: List[int], nums3: List[int], nums4: List[int]) -> int:
sums1 = {}
count = 0
for i in nums1:
for j in nums2:
if i + j in sums1:
sums1[i+j] += 1
else:
sums1[i+j] = 1
for m in nums3:
for n in nums4:
target = 0 - (m + n)
if target in sums1:
count += sums1[target]
return count
383. 赎金信
状态:结合有效的异位词的解法,自己写代码AC, 多数时间耗在思考最后的if 判断语句该怎么写
思路:题目提示只考虑小写字母,所以初始化一个长为26的全0数组count;首先统计ransomNote
中各字母出现的次数,在统计好的count上做减法(magazine
每出现一次某字母,count的对应元素就减1);最后遍历ransomNote
的所有元素,若存在某字母的count个数大于0,则返回False
总结:
1. 最后的if 判断语句的逻辑:数组count是按照ransomnote的字母出现情况计数的,与magazine的字母出现情况做减法后,若存在count[xx] > 0的情况,则说明不能从magazine中找到足够多的字母xx来构成ransomnote
2. 看过示例代码,学习到了可以用python collections模块的函数Counter 实现更快速简洁的解法
3. 时间复杂度 O(n), 空间复杂度 O(1)
class Solution:
def canConstruct(self, ransomNote: str, magazine: str) -> bool:
count = [0] * 26
for i in ransomNote:
count[ord(i) - ord('a')] += 1
for j in magazine:
count[ord(j) - ord('a')] -= 1
for i in ransomNote:
if count[ord(i) - ord('a')] > 0:
return False
return True
15. 三数之和
状态:先看视频,再自己尝试写代码,但对于去重的细节问题还没有完全掌握
思路:
一个for循环+双指针,分别代表三个数
先对数组排序,用一个for循环进行第一个数nums[i]从下标0开始的遍历,指针left从 i+1 开始,指针right从数组末尾 len(nums)-1开始;找nums[i] + nums[left] + nums[right] = 0的元组
移动双指针:(前提是升序的数组!!)当sums>0时,可将指针right向左移;当sums<0时,可将指针left向右移;若sums=0, 则保存该元组[nums[i ], nums[left], nums[right]]
去重:
1) nums[i]去重:if i > 0 and sorted_list[i] == sorted_list[i - 1]
2)nums[left]去重:while left < right and sorted_list[left] == sorted_list[left + 1]
3)nums[right]去重:while left < right and sorted_list[right] == sorted_list[right - 1]
注:left 和 right 的去重逻辑必须放在收取一个结果之后,比如考虑特殊情况:全0数组,此时的[0,0,0]是符合条件,但如果将left 和 right 的去重放在while循环最开始,[0,0,0]这样的数组就无法被获取
总结:
1. 首先必须将数组进行排序
2. 去重的逻辑一定要搞清楚,尤其是left 和 right 的去重,必须放在收取一个结果之后,且要用while而不是if判断
3. 去重判断结束后,还要对双指针进行移动操作
4. 时间复杂度O(n^2),空间复杂度O(1)
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
result = []
sorted_list = sorted(nums)
for i in range(len(nums)):
if sorted_list[i] > 0:
return result
if i > 0 and sorted_list[i] == sorted_list[i - 1]:
continue
left = i + 1
right = len(nums) - 1
while left < right:
sums = sorted_list[i] + sorted_list[left] + sorted_list[right]
if sums > 0:
right -= 1
elif sums < 0:
left += 1
else:
result.append([sorted_list[i], sorted_list[left], sorted_list[right]])
while left < right and sorted_list[right] == sorted_list[right - 1]:
right -= 1
while left < right and sorted_list[left] == sorted_list[left + 1]:
left += 1
left += 1
right -= 1
return result
18. 四数之和
状态:先看视频,再自己尝试写代码,还是细节问题:二级去重
思路:与三数之和类似,但多一层for循环,且剪枝和去重的细节问题更多
总结:
1. 剪枝过程:
一级剪枝 if nums[i] > 0 and target > 0 and nums[i] > target
二级剪枝if nums[i] + nums[j] > 0 and target > 0 and nums[i] + nums[j] > target
2. 二级去重时,j的起始点应为if j > i+1 and nums[j] == nums[j - 1]
3. 以此类推,五数之和、六数之和也可以用相似的思路解决
4. 看示例代码,还可以用dict():首先创建一个dict()来存储nums中每个数的频率,再创建一个set()来存储最终答案,并遍历4个数字的所有唯一组合。其中,对数字的重复性判断很有意思
5. 时间复杂度O(n^3),空间复杂度O(1)
class Solution:
def fourSum(self, nums: List[int], target: int) -> List[List[int]]:
nums.sort()
result = []
for i in range(len(nums)):
if nums[i] > 0 and target > 0 and nums[i] > target:
break
if i > 0 and nums[i] == nums[i - 1]:
continue
for j in range(i+1, len(nums)):
if nums[i] + nums[j] > 0 and target > 0 and nums[i] + nums[j] > target:
break
if j > i+1 and nums[j] == nums[j - 1]:
continue
left = j + 1
right = len(nums) - 1
while left < right:
sum_ = nums[i] + nums[j] + nums[left] + nums[right]
if sum_ > target:
right -= 1
elif sum_ < target:
left += 1
else:
result.append([nums[i], nums[j], nums[left], nums[right]])
while left < right and nums[left] == nums[left + 1]:
left += 1
while left < right and nums[right] == nums[right - 1]:
right -= 1
left += 1
right -= 1
return result