leetcode_015_三数之和解析

三数之和解析

题目

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

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

示例 1:
输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
解释:
nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0 。
nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0 。
nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0 。
不同的三元组是 [-1,0,1][-1,-1,2] 。
注意,输出的顺序和三元组的顺序并不重要。

示例 2:
输入:nums = [0,1,1]
输出:[]
解释:唯一可能的三元组和不为 0 。

示例 3:
输入:nums = [0,0,0]
输出:[[0,0,0]]
解释:唯一可能的三元组和为 0

暴力解法1

使用3重循环暴力解法

package main

import (
	"fmt"
	"sort"
)

// 使用3重for循环
func threeSum(nums []int) [][]int {
	sort.Ints(nums)
	result := make([][]int, 0)
	for i := 0; i < len(nums); i++ {
		for j := 0; j < len(nums); j++ {
			if i == j {
				continue
			}
			for k := 0; k < len(nums); k++ {
				if j == k || i == k {
					continue
				}
				if nums[i]+nums[j]+nums[k] == 0 {
					x := []int{nums[i], nums[j], nums[k]}
					sort.Ints(x)
					result = append(result, x)
				}
			}
		}
	}
	return removeDuplicates(result)
}

func removeDuplicates(slices [][]int) [][]int {
	seen := make(map[string]bool)
	var result [][]int

	for _, slice := range slices {
		// 将子切片转换为字符串作为map的键
		key := fmt.Sprint(slice)
		if !seen[key] {
			seen[key] = true
			result = append(result, slice)
		}
	}
	return result
}

func main() {
	nums := []int{-1, 0, 1, 2, -1, -4}
	res := threeSum(nums)
	fmt.Println(res)
}

暴力解法2

使用优化后的3重循环暴力解法

package main

import (
	"fmt"
	"sort"
)

// 使用3重for循环
func threeSum(nums []int) [][]int {
	sort.Ints(nums)
	result := make([][]int, 0)
	for i := 0; i < len(nums); i++ {
		for j := i + 1; j < len(nums); j++ {
			for k := j + 1; k < len(nums); k++ {
				if nums[i]+nums[j]+nums[k] == 0 {
					result = append(result, []int{nums[i], nums[j], nums[k]})
				}
			}
		}
	}
	return removeDuplicates(result)
}

func removeDuplicates(slices [][]int) [][]int {
	seen := make(map[string]bool)
	var result [][]int

	for _, slice := range slices {
		// 将子切片转换为字符串作为map的键
		key := fmt.Sprint(slice)
		if !seen[key] {
			seen[key] = true
			result = append(result, slice)
		}
	}
	return result
}

func main() {
	nums := []int{-1, 0, 1, 2, -1, -4}
	res := threeSum(nums)
	fmt.Println(res)
}

排序+双指针算法

  • 首先对数组进行排序,排序后固定一个数 nums[i],再使用左右指针指向 nums[i]后面的两端,数字分别为 nums[L] 和 nums[R],计算三个数的和 sum 判断是否满足为 0,满足则添加进结果集

  • 如果 nums[i]大于 0,则三数之和必然无法等于 0,结束循环

  • 如果 nums[i] == nums[i−1],则说明该数字重复,会导致结果重复,所以应该跳过

  • 当 sum == 0 时,nums[L] == nums[L+1] 则会导致结果重复,应该跳过,L++

  • 当 sum == 0 时,nums[R] == nums[R−1] 则会导致结果重复,应该跳过,R−−

  • 当 sum < 0 时,L++

  • 当 sum > 0 时,R–

双指针的左右指针移动条件。

核心思想:排序、双指针移动、跳过重复元素。

跳过重复元素:对于i,在遍历过程中,如果当前元素与前一个元素相同,则跳过它。

对于L,如果当前元素与一个元素(L+1)相同,则跳过它。

对于R,如果当前元素与一个元素(R-1)相同,则跳过它。

func threeSum(nums []int) [][]int {
	if len(nums) < 3 {
		return nil
	}
	sort.Ints(nums)
	result := make([][]int, 0)
	for i := 0; i < len(nums); i++ {
		if nums[i] > 0 {
			break
		}
		if i > 0 && nums[i] == nums[i-1] {
			continue // i去重
		}
		L := i + 1
		R := len(nums) - 1
		for L < R {
			sum := nums[i] + nums[L] + nums[R]
			if sum == 0 {
				result = append(result, []int{nums[i], nums[L], nums[R]})
				for L < R && nums[L] == nums[L+1] {
					L++ // L去重
				}
				for L < R && nums[R] == nums[R-1] {
					R-- // R去重
				}
				L++
				R--
			} else if sum < 0 {
				L++
			} else if sum > 0 {
				R--
			}
		}

	}
	return result
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

shulu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值