(LeetCode-Hot100)15. 三数之和

15. 三数之和(3Sum)

🔗 问题简介

LeetCode 中文链接

📝 题目描述

给你一个整数数组 nums,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足以下条件:

  • i != ji != kj != 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[0] + nums[1] + nums[4] = (-1) + 0 + (-1) = -2 ≠ 0
nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0

示例 2:

复制代码
输入:nums = [0,1,1]
输出:[]
解释:唯一可能的三元组和不为 0。

示例 3:

复制代码
输入:nums = [0,0,0]
输出:[[0,0,0]]
解释:唯一可能的三元组和为 0。

💡 解题思路

✅ 主流解法:排序 + 双指针(推荐)

步骤详解:
  1. 排序数组

    先对 nums 进行升序排序。这一步是为了后续使用双指针,并方便去重。

  2. 固定第一个数(外层循环)

    遍历 i0n - 3(因为至少要留两个位置给 jk):

    • 如果 nums[i] > 0,由于数组已排序,后面不可能再有和为 0 的组合,直接 break。
    • 去重处理 :如果 i > 0nums[i] == nums[i - 1],跳过,避免重复三元组。
  3. 双指针查找另外两个数

    left = i + 1right = n - 1

    • 计算当前三数之和 sum = nums[i] + nums[left] + nums[right]
    • sum == 0
      • [nums[i], nums[left], nums[right]] 加入结果
      • 去重 :移动 left 跳过相同值;移动 right 跳过相同值
      • left++, right--
    • sum < 0left++
    • sum > 0right--
  4. 返回结果


❌ 其他解法(不推荐)

方法 描述 缺点
暴力三重循环 三层 for 循环枚举所有三元组 时间复杂度 O(n³),超时
哈希表优化两重循环 固定两个数,用哈希查第三个 难以有效去重,逻辑复杂,仍可能 O(n²) 空间

💡 结论:排序 + 双指针 是最优解,兼顾时间效率与去重逻辑清晰。


💻 代码实现

java:Java 复制代码
import java.util.*;

public class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        Arrays.sort(nums);
        List<List<Integer>> res = new ArrayList<>();
        int n = nums.length;
        
        for (int i = 0; i < n - 2; i++) {
            // 如果最小值都大于0,不可能有解
            if (nums[i] > 0) break;
            
            // 跳过重复的第一个元素
            if (i > 0 && nums[i] == nums[i - 1]) continue;
            
            int left = i + 1, right = n - 1;
            while (left < right) {
                int sum = nums[i] + nums[left] + nums[right];
                if (sum == 0) {
                    res.add(Arrays.asList(nums[i], nums[left], nums[right]));
                    
                    // 跳过重复的 left 和 right
                    while (left < right && nums[left] == nums[left + 1]) left++;
                    while (left < right && nums[right] == nums[right - 1]) right--;
                    
                    left++;
                    right--;
                } else if (sum < 0) {
                    left++;
                } else {
                    right--;
                }
            }
        }
        return res;
    }
}
go:Go 复制代码
import (
    "sort"
)

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

🎯 示例演示(以 nums = [-1,0,1,2,-1,-4] 为例)

  1. 排序后:[-4, -1, -1, 0, 1, 2]
  2. i = 0-4)→ 最小值 -4,最大可能和 -4 + 1 + 2 = -1 < 0 → 无解
  3. i = 1-1):
    • left=2-1),right=52)→ -1 + (-1) + 2 = 0 → 找到 [-1,-1,2]
    • 移动指针后:left=30),right=41)→ -1 + 0 + 1 = 0 → 找到 [-1,0,1]
  4. i = 2-1)→ 与 i=1 相同,跳过(去重)
  5. i = 30)→ 0 + 1 + 2 = 3 > 0,无解

✅ 最终结果:[[-1,-1,2], [-1,0,1]]


✅ 答案有效性证明

正确性:

  • 完备性 :通过遍历所有可能的 i,并在每个 i 下用双指针覆盖所有 (left, right) 组合,确保不漏解。
  • 无重复
    • 外层 i 跳过与前一个相同的值;
    • 内层在找到解后,leftright 同时跳过重复值;
    • 保证每个三元组 (a, b, c) 满足 a ≤ b ≤ c 且唯一。

边界处理:

  • 数组长度 < 3 → 自动返回空(循环不执行)
  • 全零数组 → 正确返回 [[0,0,0]]
  • 无解情况 → 返回空列表

📊 复杂度分析

项目 分析
时间复杂度 O(n log n) + O(n²) = O(n²) 排序 O(n log n),外层循环 O(n),内层双指针 O(n)
空间复杂度 O(1)(不计输出空间) 仅使用常数额外变量(Go/Java 的排序可能用 O(log n) 栈空间)

💡 实际运行效率高,是 LeetCode 官方推荐解法。


📌 问题总结

  • 核心思想排序 + 双指针 + 去重
  • 关键技巧
    • 利用排序将三数之和转化为"两数之和"问题;
    • 通过跳过重复元素避免无效计算和重复答案;
    • 提前终止(nums[i] > 0 时 break)优化性能。
  • 适用场景:类似"K 数之和"问题(如四数之和)均可借鉴此思路。
  • 易错点
    • 忘记去重导致答案重复;
    • 指针移动顺序错误;
    • 边界条件(如数组长度不足)未处理。

github地址: https://github.com/swf2020/LeetCode-Hot100-Solutions

相关推荐
懒惰成性的2 小时前
12.Java的异常
java·开发语言
装不满的克莱因瓶2 小时前
Java7新特性:try-with-resources写法
java·前端·javascript·jdk·新特性·jdk7
程序员酥皮蛋2 小时前
hot 100 第二十七题 27.合并两个有序链表
数据结构·leetcode·链表
前路不黑暗@2 小时前
Java项目:Java脚手架项目的通用组件的封装(六)
java·开发语言·spring
BlockWay2 小时前
西甲赛程搬进平台:WEEX以竞猜开启区域合作落地
大数据·人工智能·算法·安全
马士兵教育2 小时前
程序员简历如何编写才能凸显出差异化,才能拿到更多面试机会?
开发语言·后端·面试·职场和发展·架构
chilavert3183 小时前
技术演进中的开发沉思-368:锁机制(中)
java·开发语言·jvm
~央千澈~3 小时前
抖音弹幕游戏开发之第12集:添加冷却时间机制·优雅草云桧·卓伊凡
java·服务器·前端
hqyjzsb3 小时前
企业培训ROI深度分析:如何将CAIE认证的显性与隐性成本纳入投资回报率模型
人工智能·考研·职场和发展·创业创新·学习方法·业界资讯·改行学it