Skip to content

15.三数之和

一、问题描述

给定一个包含 n 个整数的数组 nums,判断是否可以在该数组中找到三个整数,使得它们的和为 0。

示例

plaintext
输入:nums = [-1, 0, 1, 2, -1, -4]
输出:true
解释:
与 [-1, -1, 2] 相加得到 0

二、方案一:暴力法

1、思路

暴力法是最直观的方法。通过三重循环遍历数组中的所有三个数字组合,然后检查它们的和是否为 0。

2、代码实现

go
func threeSum(nums []int) [][]int {
    n := len(nums)
    result := [][]int{}
    for i := 0; i < n; i++ {
        for j := i + 1; j < n; j++ {
            for k := j + 1; k < n; k++ {
                if nums[i] + nums[j] + nums[k] == 0 {
                    result = append(result, []int{nums[i], nums[j], nums[k]})
                }
            }
        }
    }
    return result
}

3、复杂度分析

  • 时间复杂度:O(n^3),其中 n 是数组的长度。最坏的情况下可能需要对每个元素进行 n-1 次比较。
  • 空间复杂度:O(1),因为只使用了常数级别的额外空间。

三、方案二:排序 + 双指针法

1、思路

首先对数组进行排序,然后使用两个指针分别遍历数组,一个从左到右,一个从右到左。对于每个元素,我们尝试找到一个与它相加等于 0 的元素对。如果找到,则记录这个元素对。如果遍历完数组还没有找到这样的元素对,则说明不存在这样的三个数。

2、代码实现

go
func threeSum(nums []int) [][]int {
    sort.Ints(nums)
    result := [][]int{}
    for i := 0; i < len(nums)-2; i++ {
        if i > 0 && nums[i] == nums[i-1] {
            continue
        }
        left, right := i+1, len(nums)-1
        for left < right {
            sum := nums[i] + nums[left] + nums[right]
            if sum < 0 {
                left++
            } else if sum > 0 {
                right--
            } else {
                result = append(result, []int{nums[i], nums[left], nums[right]})
                left++
                right--
                for left < right && nums[left] == nums[left-1] {
                    left++
                }
                for left < right && nums[right] == nums[right+1] {
                    right--
                }
            }
        }
    }
    return result
}

3、复杂度分析

  • 时间复杂度:O(n^2),其中 n 是数组的长度。排序的时间复杂度是 O(n log n),之后的两重循环是 O(n^2)。
  • 空间复杂度:O(1),因为只使用了常数级别的额外空间。

四、总结

方案时间复杂度空间复杂度优点缺点
暴力法O(n^3)O(1)实现简单效率低
排序 + 双指针法O(n^2)O(1)效率高需要排序,增加了时间复杂度

推荐使用排序 + 双指针法,因为它在处理大规模数据时更加高效。

木川工作室 (微信:mcmc2024)