给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。
示例 1:
输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
示例 2:
输入:nums = []
输出:[]
示例 3:
输入:nums = [0]
输出:[]
提示:
0 <= nums.length <= 3000
-105 <= nums[i] <= 105
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/3sum
python
# 三数之和为0
class Solution:
def threeSum(self, nums: [int]) -> [int]:
"""
先排序,再用双指针,排序O(nlogn),寻找二三个数O(n),寻找一二三个数O(n^2),所以时间O(n),空间O(logn)
思路:整形数组,先排序,利于后面寻找三个数,每轮搜索,固定第一个数,第二个数右移,第三个数左移,比较和与target
算法流程:
1.排序数组,初始化返回数组ans []
2.遍历数组,检查当前的first指向元素是否大于0,成立则break,不可能有a+b+c=0成立,检查first是大于0即第二个数的first开始, 且与前一个元素值相同,继续下一个first
3.每次遍历寻找第一个数时,初始化third为最后一个元素,第一个元素值的相反数即为搜索本次二三个数和的目标值target
4.再次遍历数组,从first+1开始,检查second和second-1对应元素值是否相等,等则continue,继续寻找second
5.当second<third且和大于target时,由于排序,此时只有减小和才可能等于target,即third--, 重复移动third寻找合适的third,当second=third或和<=target时,break当前的while循环
6.上步break, 检查second==third, 成立则break second third,应该更新first,继续2 3 4 5 6
7.5步的while循环仅second=third或和等于小于target时break, 如果和等target则将此三个数append进ans, 如果不等target,其实就是和小于target, 即继续更新second, 重复4 5 6 7步
8.重复2-7,直到break或遍历完数组,返回ans
:param nums:
:return:
"""
nums.sort() # 为便于后面寻找,先排序
n = len(nums)
ans = [] # 初始化返回数组
for first in range(n):
if nums[first] > 0: # 由于数组已经排序,只要第一个数大于0,后面的数肯定都大于0,不可能出现a+b+c=0,直接break,就不用再去寻找后面的可能性
break
if first > 0 and nums[first] == nums[first-1]: # 第一个数在遍历到第二及以后的数时,检查是否与前一个数重复,是则continue,否则继续寻找二三个数
continue
third = n -1 # 每次更新第一个数时,third指向最后一个数
target = -nums[first] # target对第一个数取反,后面的二三个数,只需要考虑其和与target的比较,每次更新第一个数的时候,更新target
for second in range(first+1, n):
# 第二个数与上次遍历相同,继续遍历,寻找第二个数
if second > first + 1 and nums[second] == nums[second-1]:
continue
# 左指针在右指针左侧,且和大于target
while second < third and nums[second] + nums[third] > target: # 由于是排序数组,大概率应该是其和小于target的,只有当左指针在右指针左边,且和大于target,左移右指针third--,更新third
third -= 1
# 左右指针重合或左右指针数和小于等于target
if second == third: # 大概率是和小于target,此时应该更新second,和才会变大,检查左右指针是否相等,如果等,即二三数为同一个数,必须break
break
if nums[second] + nums[third] == target: # 检查二三数和是否等于target, 成立则append进ans,继续更新second
ans.append([nums[first], nums[second], nums[third]])
return ans
if __name__ == "__main__":
nums = [2,6,7,-8,3,4,2,-13,3,-5]
nums1 = []
nums2 = [0]
nums3 = [1,2]
test = Solution()
print(test.threeSum(nums)) # [[-13, 6, 7], [-8, 2, 6], [-5, 2, 3]]
print(test.threeSum(nums1)) # []
print(test.threeSum(nums2)) # []
print(test.threeSum(nums3)) # []
golang
package main
import (
"fmt"
"sort"
)
func main() {
nums := []int{2, 6, 7, -8, 3, 4, 2, -13, 3, -5}
fmt.Println(threeSum(nums))
}
// 排序+双指针
func threeSum(nums []int) [][]int {
sort.Ints(nums)
n := len(nums)
ans := [][]int{}
var first int
for first = 0; first < n; first++ {
if nums[first] > 0 {
break
}
if first > 0 && nums[first] == nums[first-1] {
continue
}
third := n - 1
target := -nums[first]
for second := first + 1; second < n-1; second++ {
if second > first+1 && nums[second] == nums[second-1] {
continue
}
for second < third && nums[second]+nums[third] > target {
third--
}
if second == third {
break
}
if nums[second]+nums[third] == target {
ans = append(ans, []int{nums[first], nums[second], nums[third]})
}
}
}
return ans
}