概述
Leetcode 454, 383, 15, 18。其中前两题是用哈希表解决,后面两题理论上能用哈希表,但时间复杂度会变的很高。LC15和LC18都和LC454有点像,但最大的区别就在于LC454是4个数组中每个里面找一个数,所以不用考虑去重的事;但是LC15和LC18却是一个数组中找3个/4个不同的数。对于LC15和LC18,用双指针的方法减少时间开销才是更正确的做法。
哈希表part1中记录的“补数”,又或者是“变加为减”的操作在LC454和LC383中都有所体现。对于加减这种可逆的运算,有时候“变加为减”可以使代码实现更为简便。位运算中,异或^
也可逆,遇到相关的题目也可注意。我觉得前序和也是这种“逆操作”的应用之一,通过两个和做差,得到一部分子串的和。
Leetcode 454. 四数相加
4个数组,乍一看很多,但可以两两拆解,分别求和。甚至也不需要两个字典,target(这里是0)减去第二组的和之后,直接在之前的字典中搜索即可。
class Solution:
def fourSumCount(self, nums1: List[int], nums2: List[int], nums3: List[int], nums4: List[int]) -> int:
hashTable = {}
for i in nums1:
for j in nums2:
if i+j not in hashTable: # 不用keys(),可以减少一些不必要的耗时
hashTable[i+j] = 1
else:
hashTable[i+j] += 1
rtn = 0
for i in nums3:
for j in nums4:
if -i-j in hashTable:
rtn += hashTable[-i-j]
return rtn
Leetcode 383. 赎金信
同样只需要一个字典,并且通过“变加为减”的方法使结果判定和代码实现更简洁。
class Solution:
def canConstruct(self, ransomNote: str, magazine: str) -> bool:
hashMagazine = {}
for i in magazine:
if i not in hashMagazine:
hashMagazine[i] = 1
else:
hashMagazine[i] += 1
for i in ransomNote:
if i in hashMagazine:
if hashMagazine[i] > 0:
hashMagazine[i] -= 1
else:
return False
else:
return False
return True
Leetcode 15. 三数之和
看着和LC454相近,但有着本质上的区别:数组数量 + 去重要求。相较于哈希表,先对数组排序后使用双指针法,则能够依据元素间的顺序更方便地剔除重复元素。
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
result = []
nums.sort()
for i in range(len(nums)):
if nums[i]>0:
return result
if i > 0 and nums[i] == nums[i-1]:
continue
left = i+1
right = len(nums)-1
while left < right:
tmpSum = nums[i] + nums[left] + nums[right]
if tmpSum < 0:
left += 1
elif tmpSum > 0:
right -= 1
else:
result.append([nums[i], nums[left], nums[right]])
# 避免重复
while right > left and nums[right] == nums[right - 1]:
right -= 1
while right > left and nums[left] == nums[left + 1]:
left += 1
right -= 1
left += 1
return result
Leetcode 18. 四数之和
与三数之和差不多的思路,多套了层循环。同时在边界条件、返回条件上有点小区别。
class Solution:
def fourSum(self, nums: List[int], target: int) -> List[List[int]]:
nums.sort()
result = []
n = len(nums)
for i in range(n):
# 避免重复
if i > 0 and nums[i] == nums[i - 1]:
continue
for j in range(i + 1, n):
# 避免重复
if j > i + 1 and nums[j] == nums[j - 1]:
continue
left, right = j + 1, n - 1
while left < right:
total = nums[i] + nums[j] + nums[left] + nums[right]
if total < target:
left += 1
elif total > target:
right -= 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