首先能想到的解法是3层循环遍历
但是首先时间复杂度为N的3次方,不尽人意,明显有优化空间
其次题中要取得的结果不能重复
已知n个数的不重复结果可以由类似冒泡的3层循环可以遍历全部结果集,结果集个数为Cn3,但是题目实例数组中存在相同的两个数,只要输入数组中存在相同数的个数>=2,必然存在有重复的结果集,因此首先想到的3层遍历无法得到预期的解
3层循环会遍历全部输入集合,要想办法跳过不处理或跳过重复的输入集合
存在相同数的输入数组在全量遍历后会得到重复输入集合的原因是相同数被遍历时其实会得到相同的结果,那解决办法就是对于相同数,想办法跳过不遍历即可;
显然不难想到,跳过相同数的办法就是将输入数组排序,在做3层遍历时,相同数跳过,即可遍历不重复的全部输入集合,判断a+b+c=0即可得到不重复的解;这里其实遍历规模正在减小;
相同数跳过的遍历模型还是N的3次方,假定设定数a=M,想办法优化数b与数c的遍历模型
这时候遍历模型要优化,似乎卡主无法思考了,但是其实前面已经把列表变为有序了,无法直接想到更优的遍历模型时,尝试把想要的结果穷举:
对于有序的输入数组[m1,m2,m3,m4,...,mn],假如存在最小值b1,b1与c1为解,因为数组有序,b1+c1=a,只有当b2>b1,c2<c1,才可能存在b2+c2=a,不难判断出下一组解b2,c2必然只存在区间(b1, c1)内;再往下穷举解b3,c3、b4,c4,基本已经可以看到新遍历模型的雏形了。
对于有序的输入数组[m1,m2,m3,m4,...,mn],指定b=m1,遍历[m2,...,mn],假如解的c值为mk(k<n),因为m1+mk=M,假如bn属于[m2,...,mn],cn属于[m(k+1),...,mn],bn>b,cn>c,bn+cn>M,也就是对于c属于[m(k+1),...,mn]与b属于全集,都不可能找到有解的b值;
这样,遍历c和b时,c值对于[m(k+1),...,mn]数值跳过不遍历,就是遍历模型的优化;
假如指定下一b1值或c1值,寻找符合解的c1或b1,也可跳过不需遍历的子集
当b值和c值相同,说明输入集合已经被全部遍历完了,回头看b与c的2层遍历模型,发现就是一个“双指针”的新遍历模型,该模型跳过不需遍历的输入集合,原来2层遍历的O(N2)复杂度优化为O(N);
遍历第一层输入数组取a值,b值与c值使用双指针模型遍历,这就是目前官方的题解。
记录一下题解
package main
import (
"fmt"
"sort"
)
func threeSum(nums []int) [][]int {
var res [][]int
sort.Ints(nums)
for a := 0; a < len(nums); a++ {
if a > 0 && nums[a-1] == nums[a] {
continue
}
c := len(nums) - 1
b := a + 1
for b < c {
if b > a+1 && nums[b-1] == nums[b] {
b++
continue
}
if c < len(nums)-1 && nums[c+1] == nums[c] {
c--
continue
}
if nums[a]+nums[b]+nums[c] > 0 {
c--
} else if nums[a]+nums[b]+nums[c] < 0 {
b++
} else {
res = append(res, []int{nums[a], nums[b], nums[c]})
b++
c--
}
}
}
return res
}
func main() {
nums := []int{-1, 0, 1, 2, -1, -4}
r := threeSum(nums)
fmt.Print(r)
}