-
思路一
BF 三重循环,每个循环对应一个加数。时间复杂度O(n³),空间复杂度O(1) -
思路二
我们此前可以以O(n)的时间复杂度做出两数之和第6题,那么我们可以遍历数组,确定了一个数字k后,两数之和的target便为-k。但是我们的twoSum数组是排序好的,所以我们第一步需要先给数组排好序。此后需要注意的有两点:- 我们在做twoSum时找到值就结束了,但是这个题目有可能一个target有多组数都符合(ex:[ -4,-1,-1,0,1,2 ] 当i=-1时,-1,2和0,1都满足)所以在twoSum函数中,while循环的判断条件有所改变
while (left < right)
,与此同时循环体中还需要过滤掉相同的值,否则就会有重复(ex:[0,0,0,0,0] 当left=1,right=4时,如果不过滤,下一次left=2,right=3,依旧会有重复的答案产生)
while (left < right && nums[left] == nums[++left]) ;
注意最后的条件是nums[++left] 如果是nums[left++],会一直为真 - 还有一个去重点体现在遍历数组中,同样是要过滤掉相同的值
while (index < nums.length - 1 && nums[index++] == nums[index])
复杂度分析
- 时间复杂度:首先排序O(nlogn),然后外层遍历数组O(n),内层调用twoSum函数,复杂度为O(n),所以整体时间复杂度为O(n²)
- 空间复杂度:没有什么额外空间,O(1)
public List<List<Integer>> threeSum(int[] nums) { List<List<Integer>> result = new ArrayList<>(); if (nums.length >= 3) { Arrays.sort(nums); int index = 0; while (index < nums.length - 2) { List<List<Integer>> sum = twoSum(nums, index + 1, -nums[index]); if (0 != sum.size()) { result.addAll(sum); } while (index < nums.length && nums[index++] == nums[index]) ; } } return result; } private List<List<Integer>> twoSum(int[] nums, int left, int target) { int right = nums.length - 1; List<List<Integer>> result = new ArrayList<>(); //遍历一遍 因为可能有多个值 while (left < right) { if (nums[left] + nums[right] > target) right--; else if (nums[left] + nums[right] < target) left++; //相等后存储起来 然后索引同时移动 else { result.add(Arrays.asList(-target, nums[left], nums[right])); while (left < right && nums[left] == nums[++left]) ; while (left < right && nums[right] == nums[--right]) ; } } return result; }
- 我们在做twoSum时找到值就结束了,但是这个题目有可能一个target有多组数都符合(ex:[ -4,-1,-1,0,1,2 ] 当i=-1时,-1,2和0,1都满足)所以在twoSum函数中,while循环的判断条件有所改变
Go版本
func twoSum(nums []int, start int, target int) (result [][]int) {
left, right := start, len(nums)-1
for left < right {
sum := nums[left] + nums[right]
if sum == target {
result = append(result, []int{-target, nums[left], nums[right]})
// 去除重复元素
for left < right && (nums[left] == nums[left+1] || nums[right] == nums[right-1]) {
if nums[left] == nums[left+1] {
left++
}
if nums[right] == nums[right-1] {
right--
}
}
left++
right--
} else if sum > target {
right--
} else {
left++
}
}
return result
}
func threeSum(nums []int) [][]int {
sort.Ints(nums)
var result [][]int
for i := 0; i < len(nums)-2; i++ {
tmp := twoSum(nums, i+1, -nums[i])
for _, sumArray := range tmp {
result = append(result, sumArray)
}
for i < len(nums)-2 && nums[i] == nums[i+1] {
i++
}
}
return result
}
可以看作twoSum的强化版,需要注意的是有两次去重,一次是在threeSum里面,target需要去重,另一次是在twoSum里面,sum==target后需要对left和right’对应的元素去重