给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组。
注意:答案中不可以包含重复的三元组。
例如, 给定数组 nums = [-1, 0, 1, 2, -1, -4],
满足要求的三元组集合为:
[
[-1, 0, 1],
[-1, -1, 2]
]
在真实的面试中遇到过这道题?
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/3sum
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
c#
法一
public class Solution {
public IList<IList<int>> ThreeSum(int[] nums)
{
IList<IList<int>>result=new List<IList<int>>();
nums=nums .OrderBy (a=>a ).ToArray ();
for(int i=0;i<nums.Length-2;i++)
{
if(i>0&&nums[i]==nums[i-1])
continue;
for(int j=i+1;j<nums.Length-1;j++)
{
if(j>i+1&&nums[j]==nums[j-1])
continue;
for(int k=j+1;k<nums.Length;k++)
{
if(k>j+1&&nums[k]==nums[k-1])
continue;
if(nums[i]+nums[j]+nums[k]==0)
result.Add(new List<int>(){nums[i],nums[j],nums[k]});
}
}
}
return result;
}
}
法二
public class Solution {
public IList<IList<int>> ThreeSum(int[] nums)
{
IList<IList<int>>result=new List<IList<int>>();
if(nums.Length<3)
return result;
nums=nums .OrderBy (a=>a ).ToArray ();
for(int i=0;i<nums.Length-2;i++)
{
if(i>0&&nums[i]==nums[i-1])
continue;
if(nums[i]>0)
return result;
int l=i+1;
int r=nums.Length-1;
while(l<r)
{
if(nums[i]+nums[l]+nums[r]==0)
{
result.Add(new List<int>(){nums[i],nums[l],nums[r]});
while(l<r&&nums[l]==nums[l+1])
l++;
while(l<r&&nums[r]==nums[r-1])
r--;
l++;
r--;
}
else if(l<r&&nums[i]+nums[l]+nums[r]>0)
r--;
else if((l<r&&nums[i]+nums[l]+nums[r]<0))
l++;
}
}
return result;
}
}
python
大体思路是遍历一边,先确定一个元素,然后剩下的两个元素就变成了两数之和的问题。
class Solution:
def threeSum(self, nums: List[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
l=i+1
r=n-1
target=-nums[i]
temp=[]
map={}
for j in range(i+1,n):
if map.get(target-nums[j]) is not None:
if [nums[i],nums[j],target-nums[j]]not in temp:
temp.append([nums[i],nums[j],target-nums[j]])
map[nums[j]]=j
result+=temp
return result
class Solution:
def threeSum(self, nums: List[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
l=i+1
r=n-1
target=-nums[i]
temp=[]
while l<r:
mid =(l+r)//2
if nums[l]+nums[mid]>target:
r=mid-1
elif nums[r]+nums[mid]<target:
l=mid+1
elif nums[l]+nums[r]==target :
if [nums[i],nums[l],nums[r]] not in temp :
temp.append([nums[i],nums[l],nums[r]])
l+=1
r-=1
elif nums[l]+nums[r]<target:
l+=1
elif nums[r]+nums[l]>target:
r-=1
result+=temp
return result
在做的过程中我发现这里面存在很多重复的项,如果我们每一次都要判断result 里面是否含有该组合,是十分浪费时间的,而我们又发现这个其实啊对于第一层遍历中遍历的不同的数,找到的组合是不会存在重复的,所以重复都是存在于里面的内一个循环,所以我单独使用了一个temp每次查重只需要考虑temp中是否有重复的就可以了。
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
nums.sort()
result =set()
n=len(nums)
for i in range(n):
if nums[i]>0:
break
if i>0 and nums[i]==nums[i-1]:
continue
l=i+1
r=n-1
target=-nums[i]
temp=set()
while l<r:
mid =(l+r)//2
if nums[l]+nums[mid]>target:
r=mid-1
elif nums[r]+nums[mid]<target:
l=mid+1
elif nums[l]+nums[r]==target :
# if [nums[i],nums[l],nums[r]] not in temp :
# temp.append([nums[i],nums[l],nums[r]])
result.add(tuple([nums[i],nums[l],nums[r]]))
while l<r and nums[l]==nums[l+1]:
l+=1
while l<r and nums[r]==nums[r-1]:
r-=1
l+=1
r-=1
elif nums[l]+nums[r]<target:
l+=1
elif nums[r]+nums[l]>target:
r-=1
# result+=temp
return list(result)
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
nums.sort()
result =[]
n=len(nums)
for i in range(n):
if nums[i]>0:
break
if i>0 and nums[i]==nums[i-1]:
continue
l=i+1
r=n-1
target=-nums[i]
temp=set()
map={}
for j in range(i+1,n):
if map.get(target-nums[j]) is not None:
# if [nums[i],nums[j],target-nums[j]]not in temp:
temp.add(tuple([nums[i],nums[j],target-nums[j]]))
map[nums[j]]=j
result+=list(temp)
return result
接下来我考虑能不能用set()来解决重复的问题,还有就是当我们第一层遍历得到的nums[i]>0时,我们就不需要进行下面的搜索了,因为剩下的数都是大于零的了,不可能三数之和等于零了,所以直接break.
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
nums.sort()
result =[]
n=len(nums)
for i in range(n):
if nums[i]>0:
break
if i>0 and nums[i]==nums[i-1]:
continue
l=i+1
r=n-1
target=-nums[i]
temp=set()
while l<r:
mid =(l+r)//2
if nums[l]+nums[mid]>target:
r=mid-1
elif nums[r]+nums[mid]<target:
l=mid+1
elif nums[l]+nums[r]==target :
# if [nums[i],nums[l],nums[r]] not in temp :
# temp.append([nums[i],nums[l],nums[r]])
result.append([nums[i],nums[l],nums[r]])
while l<r and nums[l]==nums[l+1]:
l+=1
while l<r and nums[r]==nums[r-1]:
r-=1
l+=1
r-=1
elif nums[l]+nums[r]<target:
l+=1
elif nums[r]+nums[l]>target:
r-=1
# result+=temp
return result
进一步优化再来考虑两数之和中的产生重复组合的原因是什么?
其实是因为当我们找出一组解之后l++等于l同时r++等于r,我们选择加两个while 循环来解决这种情况。
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
result=[]
map={}
for i in nums :
map[i]=map.get(i,0)+1
nums=sorted(map)
n=len(nums)
for i in range (n):
if map[nums[i]]>1:
if map[nums[i]]>2 and nums[i]==0:
result.append([0,0,0])
elif map.get(-2*nums[i]) is not None and nums[i]!=0:
result.append([nums[i],nums[i],-2*nums[i]])
if nums[i]>0:
continue
target=-nums[i]
l=bisect.bisect_left(nums,target-nums[-1],i+1)
r=bisect.bisect_right(nums,target//2,l)
for j in range(l,r):
if map.get(target-nums[j]) is not None and target!=2*nums[j]:
result.append([nums[i],nums[j],target-nums[j]])
return result
我们发现啊,很多重复的组合都是因为,这个数组里面重复的元素造成的,我们能不能把遍历的时候先把数组里面的元素去重呢,当然是可以的,那么其出现的次数应该怎么办,我们选择使用一个字典来储存数组中每个元素出现的次数。
- 其实为什么这么想呢主要还是因为我们发现当三个数字都不同时
先确定一个数字,另外两个数字的取值范围其实是被限制了,因为另外两个数字中小的那个最小也要大于target-nums[-1],而且其还要小于i+1,最大也要大于target//2因此其取值范围被进一步缩小了。
我们取值策略是先找出两个数字中更小的哪个,这样的取值策略其实也解决了会出现重复的组合的情况。
然后我们在根据找到的这个小的数字查看字典中存不存在与其相对应的更大的那个数字。 - 然后下一步我们再把组合中存在重复的元素的情况进行一下讨论,问题就解决了
一种是存在三个相同的元素,即三个0
另一种是存在两个相同的元素,跟一个等于-2*nums[i]的元素
二刷
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
ans = []
counts = {}
for i in nums:
counts[i] = counts.get(i, 0) + 1
nums = sorted(counts)
for i, num in enumerate(nums):
if counts[num] > 1:
if num == 0:
if counts[num] > 2:
ans.append([0, 0, 0])
else:
if -num * 2 in counts:
ans.append([num, num, -2 * num])
if num < 0:
two_sum = -num
left = bisect.bisect_left(nums, (two_sum - nums[-1]), i + 1)
for i in nums[left: bisect.bisect_right(nums, (two_sum // 2), left)]:
j = two_sum - i
if j in counts and j != i:
ans.append([num, i, j])
return ans
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
res=[]
nums.sort()
for i in range(len(nums)):
if i>0 and nums[i]==nums[i-1]:
continue
target=-nums[i]
dic={}
for j in range(i+1,len(nums)):
if j+1<len(nums)and nums[j]==nums[j+1]:
dic[nums[j]]=j
continue
if target-nums[j] in dic:
# if [nums[i],nums[j],target-nums[j]] not in res:
# if not res or nums[j]==nums[j-1]:
res.append([nums[i],nums[j],target-nums[j]])
else:
dic[nums[j]]=j
return res