454.四数相加II
题目
给你四个整数数组 nums1
、nums2
、nums3
和 nums4
,数组长度都是 n
,请你计算有多少个元组 (i, j, k, l)
能满足:
0 <= i, j, k, l < n
nums1[i] + nums2[j] + nums3[k] + nums4[l] == 0
思路
一开始我想着是分成两类:一类是正,一类是负。然后先在正的里面找一个,负数组里找一个,剩下三个相加,看这个结果和正数相加是否为0。然后再在负里找一个。最后还剩一类:两正两负。但是对于这种情况,我不知道怎么处理。然后看代码随想录的思路,是将前两个数组相加放到字典里,键为相加的结果,值为出现的次数。然后再将后两个数组相加得到的结果与字典进行比较。我一直不会python中字典的创建方法。
代码随想录的代码
代码
class Solution(object):
def fourSumCount(self, nums1, nums2, nums3, nums4):
# 使用字典存储nums1和nums2中的元素及其和
hashmap = dict()
for n1 in nums1:
for n2 in nums2:
if n1 + n2 in hashmap:
hashmap[n1+n2] += 1
else:
hashmap[n1+n2] = 1
# 如果 -(n1+n2) 存在于nums3和nums4, 存入结果
count = 0
for n3 in nums3:
for n4 in nums4:
key = - n3 - n4
if key in hashmap:
count += hashmap[key]
return count
这个思路还是要学习的以及对于字典的处理方法。
383. 赎金信
题目
给你两个字符串:ransomNote
和 magazine
,判断 ransomNote
能不能由 magazine
里面的字符构成。
如果可以,返回 true
;否则返回 false
。
magazine
中的每个字符只能在 ransomNote
中使用一次。
尝试解答
这道题和有效的字母异位词类似,找magazine里是否存在ransomNote。同样也是只有26个小写字母,可以考虑用数组解答。
class Solution(object):
def canConstruct(self, ransomNote, magazine):
"""
:type ransomNote: str
:type magazine: str
:rtype: bool
"""
nums1=[0]*26
nums2=[0]*26
for i in ransomNote:
nums1[ord(i)-ord("a")]+=1
for i in magazine:
nums2[ord(i)-ord("a")]+=1
for i in range(26):
if nums2[i]<nums1[i]:
return False
return True
代码随想录的代码
代码
class Solution:
def canConstruct(self, ransomNote: str, magazine: str) -> bool:
ransom_count = [0] * 26
magazine_count = [0] * 26
for c in ransomNote:
ransom_count[ord(c) - ord('a')] += 1
for c in magazine:
magazine_count[ord(c) - ord('a')] += 1
return all(ransom_count[i] <= magazine_count[i] for i in range(26))
15. 三数之和
题目
给你一个整数数组 nums
,判断是否存在三元组 [nums[i], nums[j], nums[k]]
满足 i != j
、i != k
且 j != k
,同时还满足 nums[i] + nums[j] + nums[k] == 0
。请
你返回所有和为 0
且不重复的三元组。
注意:答案中不可以包含重复的三元组。
尝试解答
大致思路是先把数组中的数与除了自身的数都相加一遍,然后看数组里是否存在这个结果的相反数。
class Solution(object):
def threeSum(self, nums):
"""
:type nums: List[int]
:rtype: List[List[int]]
"""
nums1=[]
for i in range(len(nums)):
for n2 in nums[i+1:]:
a=-(nums[i]+n2)
if a in nums:
nums1.append([nums[i],n2,a])
return nums1
这段代码结果不对,输出的是重复的结果。
代码随想录的代码
代码1(双指针法)
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
result = []
nums.sort()
for i in range(len(nums)):
# 如果第一个元素已经大于0,不需要进一步检查
if nums[i] > 0:
return result
# 跳过相同的元素以避免重复
if i > 0 and nums[i] == nums[i - 1]:
continue
left = i + 1
right = len(nums) - 1
while right > left:
sum_ = nums[i] + nums[left] + nums[right]
if sum_ < 0:
left += 1
elif sum_ > 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
代码随想录中也说了
“两层for循环就可以确定 a 和b 的数值了,可以使用哈希法来确定 0-(a+b) 是否在 数组里出现过,其实这个思路是正确的,但是我们有一个非常棘手的问题,就是题目中说的不可以包含重复的三元组。
把符合条件的三元组放进vector中,然后再去重,这样是非常费时的,很容易超时,也是这道题目通过率如此之低的根源所在。
去重的过程不好处理,有很多小细节,如果在面试中很难想到位。
时间复杂度可以做到O(n^2),但还是比较费时的,因为不好做剪枝操作。
大家可以尝试使用哈希法写一写,就知道其困难的程度了。”
也行,至少哈希法的思路掌握了。
代码随想录推荐双指针法,
思路1:两个指针,一个指头一个指尾。再来一个指针遍历它们之间的元素。但其实我不知道这会不会重复,先写个试试吧----不对,确实没重复,但是没有遍历完
思路2:两个指针,一个指头固定不动,另一个指尾,逐渐减小。再来一个指针遍历它们之间的元素,除了[0,0,0]外,正确。关键就是去重
还是看看代码随想录的代码吧
学习
开始可以sort排序一下,如果首位大于0,直接输出空-----没想过
而且排序之后,去重就比较简单,直接跳过相同元素就行。一共是三次跳过,可以理解成三个指针吧,左右两个,还有一个遍历的。所以判读三次,首先是对于遍历指针的判断跳过,如果存在重复元素,就跳过。然后是对于左右指针的判断,重复就跳过。
而对于指针的移动,当和小于0时,移动左指针。当和大于0时,移动右指针。-----感觉这个好熟悉,翻看了一下,与二分查找有点类似。
代码2(字典)
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. 四数之和
题目
给你一个由 n
个整数组成的数组 nums
,和一个目标值 target
。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]]
(若两个四元组元素一一对应,则认为两个四元组重复):
0 <= a, b, c, d < n
a
、b
、c
和d
互不相同nums[a] + nums[b] + nums[c] + nums[d] == target
你可以按 任意顺序 返回答案 。
尝试解答
和三数之和差不多,双指针法。但是和四数相加II不同,这个不需要去重。三数之和、四数之和需要去重。
class Solution(object):
def fourSum(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: List[List[int]]
"""
nums.sort()
n=len(nums)
result=[]
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=j+1
right=n-1
while right>left:
a=nums[i]+nums[j]+nums[left]+nums[right]
if a>target:
right-=1
elif a<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
代码随想录的代码
代码
class Solution:
def fourSum(self, nums: List[int], target: int) -> List[List[int]]:
nums.sort()
n = len(nums)
result = []
for i in range(n):
if nums[i] > target and nums[i] > 0 and target > 0:# 剪枝(可省)
break
if i > 0 and nums[i] == nums[i-1]:# 去重
continue
for j in range(i+1, n):
if nums[i] + nums[j] > target and target > 0: #剪枝(可省)
break
if j > i+1 and nums[j] == nums[j-1]: # 去重
continue
left, right = j+1, n-1
while left < right:
s = nums[i] + nums[j] + nums[left] + nums[right]
if s == target:
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
elif s < target:
left += 1
else:
right -= 1
return result
差不多