454. 四数相加II
-
# 两两处理,先统计nums1+nums2,再遍历nums3和nums4 # 空间复杂度O(n2),时间复杂度O(n2)--->也是空间换时间了,不然暴力要O(n4) # 只需要次数,因此统计次数就可以了
class Solution:
def fourSumCount(self, nums1: List[int], nums2: List[int], nums3: List[int], nums4: List[int]) -> int:
add_count = dict()
n = len(nums1)
for i in range(n):
for j in range(n):
tmp = nums1[i]+nums2[j]
add_count[tmp] = add_count.get(tmp, 0) + 1
count0 = 0
for i in range(n):
for j in range(n):
tmp = 0 - nums3[i] - nums4[j]
count0 += add_count.get(tmp, 0)
return count0
383. 赎金信
一些简便写法:
- 一句话py:return not Counter(ransomNote) - Counter(magazine)
- 一句话py:return all(ransomNote.count© <= magazine.count© for c in set(ransomNote))
- tip:dict需要维护红黑树或哈希表,所以纯字母计数的情况可能用数组[0]*26会比较快。
- 但是py里面的Counter也是专门为字母计数情况写的,不知道是不是区分大小写
- 此类题目要注意是否区分大小写,能否重复使用
class Solution:
def canConstruct(self, ransomNote: str, magazine: str) -> bool:
# ransomNote里的字母次数均小于magazine里的
count1 = Counter(ransomNote)
count2 = Counter(magazine)
for i in count1:
if count1.get(i) > count2.get(i, 0):
return False
return True
15. 三数之和
- 去重的细节好多啊,感觉不好想。
- nums[i]和nums[i-1]进行比较,这是因为在对i-1进行双指针遍历的时候,就已经做完了nums[i]所有情况。
- 在找到一个三元组之后,对left和right进行去重。重复的直接跳过。这个倒是比较直观。
- 找到一个三元组后,left++的同时right–,这是因为如果只动一个但仍满足条件的话,说明是重复的。
- 其实没有想过是不是能证明这样子做就一定不会出现重复
- 双指针的做法建立在排序的基础上。i遍历数组,是固定的,left和right两个指针进行移动。时间复杂度O(n2)。
- 本来想着用set()去重,但是三元组list不能作为key,不知道可不可以转换成str后进行去重。可能会多费一点空间,需要一个str对应三元组的dict。
- 双指针在两数之和可以使用的理由是,不需要下标,如果需要下标就不行了。或者可以建立一个dict进行查找?但是数是有重复的,所以还是不行。
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
# 不可以重复,去重,将三元组按从小到大的顺序排,再用set?
# 双指针
n = len(nums)
output = []
nums.sort()
for i in range(n-2): # 0 - n-3
left = i+1
right = n-1
if i > 0 and nums[i] == nums[i-1]: # ?为啥这里能跳过呢
continue
while(left < right):
sum = nums[i] + nums[left] + nums[right]
if sum < 0:
left += 1
elif sum > 0:
right -= 1
else:
output.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
left += 1
right -= 1
return output
代码随想录的解答:这个没怎么看懂怎么去重的。
- 元素b去重为什么需要比较两个??
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
result = []
nums.sort()
# 找出a + b + c = 0
# a = nums[i], b = nums[j], c = -(a + b)
for i in range(len(nums)):
# 排序之后如果第一个元素已经大于零,那么不可能凑成三元组
if nums[i] > 0:
break
if i > 0 and nums[i] == nums[i - 1]: #三元组元素a去重
continue
d = {}
for j in range(i + 1, len(nums)):
if j > i + 2 and nums[j] == nums[j-1] == nums[j-2]: # 三元组元素b去重
continue
c = 0 - (nums[i] + nums[j])
if c in d:
result.append([nums[i], nums[j], c])
d.pop(c) # 三元组元素c去重
else:
d[nums[j]] = j
return result
18. 四数之和
- 和三数之和一个思路:双指针+去重。去重要注意每个数字都要去重,在哪里去重以及怎么去重需要考虑
- 字典法:list不能作为dict的key,但是tuple可以,因此可以把list先排序后转为tuple,再用集合set来实现去重,最后输出的时候要把tuple转回list。
class Solution:
def fourSum(self, nums: List[int], target: int) -> List[List[int]]:
n = len(nums)
output = []
nums.sort() # 排序
for i in range(n-3):
# 去重
if i > 0 and nums[i] == nums[i-1]:
continue
for j in reversed(range(i+1, n)):
# 去重
if j < n-1 and nums[j] == nums[j+1]:
continue
left = i + 1
right = j - 1
while(left < right):
sum_ = nums[i] + nums[j] + nums[left] + nums[right]
if sum_ == target:
output.append([nums[i], nums[left], nums[right], nums[j]])
# 去重
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
elif sum_ > target:
right -= 1
else:
left += 1
return output
字典解法:
class Solution:
def fourSum(self, nums: List[int], target: int) -> List[List[int]]:
# 字典解法:
freq = {}
n = len(nums)
for num in nums:
freq[num] = freq.get(num, 0) + 1 # 计算频率
ans = set()
for i in range(n):
for j in range(i+1, n):
for k in range(j+1, n):
val = target - (nums[i] + nums[j] + nums[k])
if val in freq:
count = (nums[i] == val) + (nums[j] == val) + (nums[k] == val)
if freq[val] > count:
ans.add(tuple(sorted([nums[i], nums[j], nums[k], val])))
# 用tuple就可以作为key了??
return [list(x) for x in ans]
文章讲述了如何使用双指针和去重技巧解决四数之和问题,涉及空间复杂度、时间复杂度分析,以及如何利用Python的Counter和排序功能优化解决方案。

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



