Leetcode 15 3Sum
题目描述
Given an array nums of n integers, are there elements a, b, c in nums such that a + b + c = 0? Find all unique triplets in the array which gives the sum of zero.
Note:
The solution set must not contain duplicate triplets.
Example:
Given array nums = [-1, 0, 1, 2, -1, -4],
A solution set is:
[
[-1, 0, 1],
[-1, -1, 2]
]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/3sum
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
题目解析
- 思路一:暴力穷举所有的可能性,最后做一个去重处理,但这种算法时间复杂度为 O ( n 3 ) O(n^3) O(n3),显然不能作为我们的解决方案
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
if nums is None or len(nums) < 3:
return []
length = len(nums)
nums.sort()
result = []
for i in range(0, length):
for j in range(i + 1, length):
for k in range(j + 1, length):
if nums[i] + nums[j] + nums[k] == 0:
result.append([nums[i], nums[j], nums[k]])
# 去重
result_temp = []
for item in result:
if item not in result_temp:
result_temp.append(item)
return result_temp
注意:[[],[],[]]这样的列表是无法转换为set来去重的。!
待解决:
- 列表中每个元素不是唯一的
- 重复,顺序不同的重复
O ( n 3 ) O(n^3) O(n3)时间复杂度太高,我们可以稍作改动,得到一个时间复杂度为 O ( n 2 ) O(n^2) O(n2)的算法:
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
if nums is None or len(nums) < 3:
return []
length = len(nums)
nums.sort()
result = []
for i in range(0, length):
for j in range(i + 1, length):
temp = -1 * (nums[i] + nums[j])
if temp in nums[j+1:]:
result.append([nums[i], nums[j], temp])
# 去重
result_temp = []
for item in result:
if item not in result_temp:
result_temp.append(item)
return result_temp
遍历列表中的每个元素,利用双指针在该元素的后面查找使三数之和为0的组合,时间复杂度为 O ( N 2 ) O(N^2) O(N2),但是还是超时。
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
if nums is None or len(nums) < 3:
return []
length = len(nums)
nums.sort()
result = []
for i in range(length):
left = i + 1
right = length - 1
while left < right:
sum = nums[i] + nums[left] + nums[right]
if sum == 0:
result.append([nums[i], nums[left], nums[right]])
left += 1
right -= 1
elif sum < 0:
left += 1
else: right -= 1
# 去重
result_temp = []
for item in result:
if item not in result_temp:
result_temp.append(item)
return result_temp
上述代码有很多可以优化的地方,通过一些边界条件的判断可以省去很多不必要的操作,从而提高效率。
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
if nums is None or len(nums) < 3:
return []
length = len(nums)
nums.sort()
result = []
for i in range(length - 2): # 后续元素少于三个,不用判断
if nums[i] > 0: # 首元素大于0,三数之和必定大于0
break
if nums[i] > 0 and nums[i] == nums[i - 1]: # 避免重复
continue
l = i + 1
r = length - 1
while l < r:
sum = nums[i] + nums[l] + nums[r]
if sum == 0:
result.append([nums[i], nums[l], nums[r]])
l += 1
r -= 1
while l < r and nums[l] == nums[l - 1]: l += 1 # 去重
while l < r and nums[r] == nums[i + 1]: r -= 1 # 去重
elif sum < 0:
l += 1
while l < r and nums[l] == nums[l - 1]: l += 1 # 去重
else:
r -= 1
while l < r and nums[r] == nums[r + 1]: r -= 1 # 去重
return result
注意特殊用例:[0, 0, 0, 0]、[0, 0, 0]
从上面的解题过程中我们可以看到,暴力法时间复杂度高,但不用判断很多边界条件,而双指针法通过双指针以及增加很多边界条件的判断,省去了很多不必要的操作,从而使算法的效率提高。
感觉常规解题时,总是先有一个解决方案,接着针对各种用例,以及原解决方案存在的问题,一步步添加边界条件、转化思路去优化我们的解决方案,从而提高算法的效率,得到一种相对较好的解决方案。