LeetCode 15.三数之和【中等】

109 阅读2分钟

题干

给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != ji != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请你返回所有和为 0 且不重复的三元组。

注意: 答案中不可以包含重复的三元组。

题解 -- 双指针

找到三数之和为零的所有解,可以等价的替换为”找到两数之和等于第三数的负值的所有解“,这就可以使用LeetCode 167的解法来解决这个问题了。 要注意的有两点:

  1. 本题中数组是无序的,为了套用LeetCode167的解法,我们首先要进行排序。其实为什么两数之和要求返回元素下标,而三数之和要求返回元素的值,就是因为这里要排序,排过序之后,元素下标肯定和输入的值不一样了。
  2. 题目要求答案中不可以包含重复的三元组,因此相较于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))

}