LeetCode15 三数之和

107 阅读2分钟

leetcode.cn/problems/3s…

image.png

解法一:两数之和解法扩展

对原数组排序后,固定一个数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)