题干
给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请你返回所有和为 0 且不重复的三元组。
注意: 答案中不可以包含重复的三元组。
题解 -- 双指针
找到三数之和为零的所有解,可以等价的替换为”找到两数之和等于第三数的负值的所有解“,这就可以使用LeetCode 167的解法来解决这个问题了。 要注意的有两点:
- 本题中数组是无序的,为了套用LeetCode167的解法,我们首先要进行排序。其实为什么两数之和要求返回元素下标,而三数之和要求返回元素的值,就是因为这里要排序,排过序之后,元素下标肯定和输入的值不一样了。
- 题目要求答案中不可以包含重复的三元组,因此相较于LeetCode 167还多了一个去重的操作。
- 时间复杂度: O(n^2)(排序的时间复杂度为O(nlogn),算法时间复杂度为O(n^2), O(n^2 + nlogn)=O(n^2))
- 空间复杂度: O(logn)(排序的空间复杂度为O(logn), 算法空间复杂度为O(1),O(logn + 1)=O(logn))
import (
"fmt"
"sort"
)
func threeSum(nums []int) [][]int {
sort.Ints(nums)
res := [][]int{}
// 固定第一个数,将目标和设置为第一个数的负数,套用两数之和的双指针算法
for i := range nums {
remain := -nums[i]
if i-1 >= 0 && nums[i] == nums[i-1] {
continue
}
j := i + 1
k := len(nums) - 1
// 两数之和
for j < k {
sum := nums[j] + nums[k]
if sum == remain {
res = append(res, []int{nums[i], nums[j], nums[k]})
}
// 这里和两数之和算法不同
// 左右指针移动的时候,为了去除重复的解,要移动到下一个不同的数
if sum < remain {
j++
for j < k && nums[j] == nums[j-1] {
j++
}
} else {
k--
for j < k && nums[k] == nums[k+1] {
k--
}
}
}
}
return res
}
func main() {
nums := []int{-1, 0, 1, 2, -1, -4}
fmt.Println(threeSum(nums))
nums = []int{0, 1, 1}
fmt.Println(threeSum(nums))
nums = []int{0, 0, 0}
fmt.Println(threeSum(nums))
nums = []int{0, 0, 0, 0}
fmt.Println(threeSum(nums))
nums = []int{-1, 0, 1, 2, -1, -4}
fmt.Println(threeSum(nums))
}