三数之和
参考资料: LeetCode评论
题目描述
给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?找出所有满足条件且
不重复的三元组。
注意:答案中不可以包含重复的三元组。
例如, 给定数组 nums = [-1, 0, 1, 2, -1, -4],
满足要求的三元组集合为:
[
[-1, 0, 1],
[-1, -1, 2]
]
解题方法
方法一:
思路:
双重循环,将三数之和转化为求两数之和问题;
对nums排序,每次循环进行一次去重
结果:
显而易见,超时了…
def threeSum(self, nums):
result = []
nums.sort()
print(nums)
for i in range(0, len(nums)):
if i==0 or nums[i]!=nums[i-1]:
for j in range(i+1, len(nums)):
if j==i+1 or nums[j]!=nums[j-1]:
if 0-nums[i]-nums[j] in nums[(j+1):]:
result.append([nums[i], nums[j],0-nums[i]-nums[j]])
return result
方法二:
思路:
接着上面的思路
根据题目特点,将三数之和的情况分位三种:[0, 0, 0] , [pos,0,neg] , [pos, pos, neg]or[neg, pos, pos]
但是实际上算法复杂度应该是没有变的
结果:
显而易见, 还是超时了…!!!!
class Solution:
def threeSum(self, nums):
if (not nums) or len(nums)<3 : return []
pos_num = [num for num in nums if num>0]
neg_num = [num for num in nums if num<0]
zero_num = [num for num in nums if num==0]
neg_num.sort()
pos_num.sort()
result=[]
if len(zero_num)>0:
for i in range(0, len(pos_num)):
if pos_num[i] not in pos_num[:i]:
if (0-pos_num[i]) in neg_num:
result.append([0, pos_num[i], (0-pos_num[i])])
if len(zero_num)>=3:
result.append([0, 0, 0])
for i in range(0, len(pos_num)-1):
if pos_num[i] not in pos_num[:i]:
for j in range(i+1, len(pos_num)):
if pos_num[j] not in pos_num[i+1:j]:
num3 = 0 - pos_num[i] - pos_num[j]
if num3 in neg_num:
result.append([pos_num[i], pos_num[j], num3])
for i in range(0, len(neg_num)):
if neg_num[i] not in neg_num[:i]:
for j in range(i+1, len(neg_num)):
if neg_num[j] not in neg_num[i+1:j]:
num3 = 0 - neg_num[i] - neg_num[j]
if num3 in pos_num:
result.append([neg_num[i], neg_num[j], num3])
return result
方法三:
思路:
接上面的思路
重新考虑去重问题,直接使用不重复且排序的nums, 在最后一层循环中考虑重复问题
结果:
终于不超时了, 但是执行时间和内存消耗都不佳…
class Solution:
def threeSum(self, nums):
result = []
nums_dict = {}
for num in nums:
nums_dict[num] = nums_dict.get(num, 0)+1
print(nums_dict)
if (0 in nums_dict) and nums_dict[0]>=3:
result.append([0, 0, 0])
nums = sorted(list(nums_dict.keys()))
for i, num1 in enumerate(nums):
for num2 in nums[i+1:]:
if nums_dict[num1]>=2 and num1*2+num2==0:
result.append([num1, num1, num2])
if nums_dict[num2]>=2 and num2*2+num1==0:
result.append([num1, num2, num2])
num3 = 0-num1-num2
if num3>num2 and num3 in nums_dict:
result.append([num1, num2, num3])
return result
方法四:
同样将问题转化为求两数之和问题,但是不用双重循环, 而是用两端逼近的方法:
在一重循环下,即确定数组中第一个数的情况下, 对剩下的数通过排序后,通过比较前后两端数以及num0之和 与 0 的差异,
确定逼近的节奏。
同样通过两次去重。
class Solution:
def threeSum(self, nums):
result = []
nums.sort()
print(nums)
if len(nums)<3: return []
for i, num0 in enumerate(nums):
if i>0 and num0==nums[i-1]: continue #去重
left_index = i+1
right_index = len(nums)-1
while left_index<right_index:
sum3 = nums[left_index]+nums[right_index]+num0
#print([num0, nums[left_index], nums[right_index]], sum3)
if sum3==0:
result.append([num0, nums[left_index], nums[right_index]])
while(left_index<len(nums)-1 and nums[left_index] == nums[left_index+1]): left_index+=1 # 去重
while(right_index>1 and nums[right_index] == nums[right_index-1]): right_index-=1 # 去重
left_index+=1
right_index-=1
elif sum3<0:
left_index+=1
else :
right_index-=1
return result