解法一:两数之和解法扩展
对原数组排序后,固定一个数n,在剩余的数组中找到两数之和为0-n的子数组
func threeSum(nums []int) [][]int {
res := make([][]int, 0)
if len(nums) < 3{
return res
}
// 排序后,固定一个数n,在剩余的数组中找到两数之和为0-n的子数组
sort.Ints(nums)
for i:=0; i<len(nums); i++{
if i > 0 && nums[i-1] == nums[i]{
continue
}
ans := twoSum(nums[i+1:], 0-nums[i])
for _, an := range ans{
an = append(an, nums[i]) // 加上当前固定的数n,形成三元组
res = append(res, an)
}
}
return res
}
func twoSum(nums []int, target int) [][]int{
res := make([][]int, 0)
memo := make(map[int]struct{})
used := make(map[[2]int]struct{}) // 注意,由于结果记录的是值而不是idx,所以需要去重
for _, num := range nums{
if _, ok := memo[target-num]; ok{
pair := [2]int{target-num, num}
if _, ok := used[pair]; !ok{
res = append(res, []int{num, target-num})
used[pair] = struct{}{}
}
}else{
memo[num] = struct{}{}
}
}
return res
}
- 时间复杂度:O(N^2),排序+双层遍历, 排序的复杂度为 O(NlogN),
twoSum函数中的for循环哈希判断为 O(N),threeSum函数在 for 循环中调用twoSum所以总的时间复杂度是 O(NlogN+N^2)=O(N^2)。 - 空间复杂度:O(N)
解法二:双指针
func threeSum(nums []int) [][]int {
res := make([][]int, 0)
if len(nums) < 3 {
return res
}
// 排序后,固定一个数n,在剩余的数组中找到两数之和为0-n的子数组
sort.Ints(nums)
for i := 0; i < len(nums)-2; i++ { // 因为要预留最后一个三元组,因此只需要遍历到倒数第3个数
// 针对第一个数去重,因为已经排好序,相同的元素会挨在一起
if i > 0 && nums[i] == nums[i-1] {
continue
}
target := 0 - nums[i]
// 双指针法,在剩余数组中找到两数之和
left := i + 1
right := len(nums) - 1
for left < right {
if nums[left]+nums[right] > target {
right--
} else if nums[left]+nums[right] < target {
left++
} else { // 找到一个答案
res = append(res, []int{nums[i], nums[left], nums[right]})
// 双指针相向而行,继续寻找其它答案
left++
// 针对第二个数去重(注意:可能有多个,需要for循环)
for left < right && nums[left] == nums[left-1] {
left++
}
right--
// 针对第三个数去重
for left < right && nums[right] == nums[right+1] {
right--
}
}
}
}
return res
}
- 时间复杂度:O(N^2)
- 空间复杂度:O(1)