Leecode#15刷题之三数之和

题目

给你一个整数数组 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[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 。

提示:

  • 3 <= nums.length <= 3000
  • -105 <= nums[i] <= 105

Java语言

java 复制代码
class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> result = new ArrayList<>();
        Arrays.sort(nums);

        for (int i = 0; i < nums.length - 2; i++) {
            // 跳过重复的第一个数
            if (i > 0 && nums[i] == nums[i - 1]) continue;

            // 剪枝:最小的三个数都 > 0,不可能和为 0
            if (nums[i] + nums[i + 1] + nums[i + 2] > 0) break;
            // 剪枝:当前数 + 最大的两个数都 < 0,跳过当前数
            if (nums[i] + nums[nums.length - 2] + nums[nums.length - 1] < 0) continue;

            int left = i + 1, right = nums.length - 1;
            while (left < right) {
                int sum = nums[i] + nums[left] + nums[right];

                if (sum < 0) {
                    left++;
                } else if (sum > 0) {
                    right--;
                } else {
                    result.add(Arrays.asList(nums[i], nums[left], nums[right]));

                    // 跳过重复的 left
                    while (left < right && nums[left] == nums[left + 1]) left++;
                    // 跳过重复的 right
                    while (left < right && nums[right] == nums[right - 1]) right--;

                    left++;
                    right--;
                }
            }
        }

        return result;
    }
}

核心思路: 排序 + 双指针。先排序,固定一个数 nums[i],然后用双指针在剩余部分找两数之和等于 -nums[i]


逐行说明:

java 复制代码
List<List<Integer>> result = new ArrayList<>();

Arrays.sort(nums);

创建结果列表。先排序是双指针的前提,排序后重复值相邻,也方便去重和剪枝。

java 复制代码
for (int i = 0; i < nums.length - 2; i++) {

遍历第一个数,到 length - 2 停止(至少留两个位置给双指针)。

java 复制代码
if (i > 0 && nums[i] == nums[i - 1]) continue;

去重 :如果当前数和前一个相同,跳过,避免产生重复三元组。例如 [-1,-1,0,1],第一个 -1 找了 [-1,0,1],第二个 -1 跳过。

java 复制代码
if (nums[i] + nums[i + 1] + nums[i + 2] > 0) break;

if (nums[i] + nums[nums.length - 2] + nums[nums.length - 1] < 0) continue;

剪枝优化

  • 最小的三个数之和都 > 0,后面不可能有更小的了,直接 break
  • 当前数加上最大的两个数都 < 0,当前数太小,continue 换下一个
java 复制代码
int left = i + 1, right = nums.length - 1;

while (left < right) {

int sum = nums[i] + nums[left] + nums[right];

双指针,lefti+1 开始,right 从末尾开始。计算三数之和。

java 复制代码
if (sum < 0) {

left++;

} else if (sum > 0) {

right--;

} else {

result.add(Arrays.asList(nums[i], nums[left], nums[right]));
  • 和 < 0:太小了,left 右移让和变大
  • 和 > 0:太大了,right 左移让和变小
  • 和 == 0:找到一组,加入结果
java 复制代码
while (left < right && nums[left] == nums[left + 1]) left++;

while (left < right && nums[right] == nums[right - 1]) right--;

left++;

right--;

去重 :跳过与当前 left/right 相同的值,然后各移动一步继续找下一组。


示例 1 演示: nums = [-1,0,1,2,-1,-4] → 排序后 [-4,-1,-1,0,1,2]

i numsi left right sum 操作 result
0 -4 1 5 -4+(-1)+2=-3 <0, left++ \[\]
-4 2 5 -4+(-1)+2=-3 <0, left++ \[\]
-4 3 5 -4+0+2=-2 <0, left++ \[\]
-4 4 5 -4+1+2=-1 <0, left++ → left>=right 退出 \[\]
1 -1 2 5 -1+(-1)+2=0 找到 \[-1,-1,2]
-1 3 4 -1+0+1=0 找到 \[-1,-1,2,-1,0,1]
2 -1 (与i=1相同,skip) continue
3 0 4 5 0+1+2=3 >0, right-- → 退出

最终返回 [[-1,-1,2],[-1,0,1]]

时间复杂度 O(n²),空间复杂度 O(1)(不计排序)。

python语言

python 复制代码
class Solution(object):
    def threeSum(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        result = []
        nums.sort()

        for i in range(len(nums) - 2):
            # 跳过重复的第一个数
            if i > 0 and nums[i] == nums[i - 1]:
                continue

            # 剪枝:最小的三个数都 > 0,不可能和为 0
            if nums[i] + nums[i + 1] + nums[i + 2] > 0:
                break
            # 剪枝:当前数 + 最大的两个数都 < 0,跳过当前数
            if nums[i] + nums[-2] + nums[-1] < 0:
                continue

            left, right = i + 1, len(nums) - 1
            while left < right:
                total = nums[i] + nums[left] + nums[right]

                if total < 0:
                    left += 1
                elif total > 0:
                    right -= 1
                else:
                    result.append([nums[i], nums[left], nums[right]])

                    # 跳过重复的 left
                    while left < right and nums[left] == nums[left + 1]:
                        left += 1
                    # 跳过重复的 right
                    while left < right and nums[right] == nums[right - 1]:
                        right -= 1

                    left += 1
                    right -= 1

        return result

逻辑与 Java 版完全一致,逐行说明:

python 复制代码
result = []

nums.sort()

创建结果列表,nums.sort() 原地排序。对应 Java 的 Arrays.sort(nums)

python 复制代码
for i in range(len(nums) - 2):

if i > 0 and nums[i] == nums[i - 1]:

continue

遍历第一个数。nums[i] == nums[i - 1] 跳过重复值,对应 Java 的 nums[i] == nums[i - 1]

python 复制代码
if nums[i] + nums[i + 1] + nums[i + 2] > 0:

break

if nums[i] + nums[-2] + nums[-1] < 0:

continue

剪枝优化。nums[-2]nums[-1] 是 Python 的负索引,直接取倒数第二和最后一个元素,比 Java 的 nums[nums.length - 2] 简洁很多。

python 复制代码
left, right = i + 1, len(nums) - 1

while left < right:

total = nums[i] + nums[left] + nums[right]

双指针。left, right = i + 1, len(nums) - 1 一行同时赋值两个变量。

python 复制代码
if total < 0:

left += 1

elif total > 0:

right -= 1

else:

result.append([nums[i], nums[left], nums[right]])
  • 和 < 0:left 右移
  • 和 > 0:right 左移
  • 和 == 0:result.append([a, b, c]) 添加列表,对应 Java 的 Arrays.asList(a, b, c)
python 复制代码
while left < right and nums[left] == nums[left + 1]:

left += 1

while left < right and nums[right] == nums[right - 1]:

right -= 1

left += 1

right -= 1

跳过重复值后,各移动一步继续找。+= 1 / -= 1 对应 Java 的 ++ / --


与 Java 版的关键区别:

特性 Java Python
排序 Arrays.sort(nums) nums.sort()
列表长度 nums.length len(nums)
添加元素 result.add(Arrays.asList(...)) result.append([...])
负索引 nums[nums.length - 1] nums[-1]
双变量赋值 int left = i+1, right = n-1; left, right = i+1, n-1
自增/自减 left++ / right-- left += 1 / right -= 1

时间复杂度 O(n²),空间复杂度 O(1)(不计排序)。

相关推荐
shirsl1 小时前
算法 Day1-数组 / 哈希 + 双指针
python·算法·哈希算法
2601_967760788 小时前
2026年PDF压缩与页码添加工具技术实测:性能、算法与本地化适配深度对比
算法·pdf
不会就选b9 小时前
算法日常・每日刷题--<贪心>7
数据结构·算法·leetcode
moonrailgun9 小时前
用 Node.js 复刻 Codex Astra 的终端星光
前端·javascript·算法
罗西的思考10 小时前
[Agent Memory / 强化学习] MemPO源码学习笔记 ---(1)--- 总体
人工智能·算法·机器学习
lvwangshu10 小时前
图论:LCA、树的直径、树的重心、二分图与 Tarjan 缩点
算法·图论
后台模板学习11 小时前
2026最新面试真题:到达用英语怎么说背后的逻辑与源码剖析
面试·职场和发展·php
CoderYanger11 小时前
前端基础——JavaScript(WebAPI)(下篇)
java·开发语言·前端·javascript·程序人生·面试·职场和发展
302wanger11 小时前
干与湿:AI 拿走脑力之后,人剩下什么
算法
计算机编程-吉哥12 小时前
脑肿瘤MRI智能识别系统:基于深度学习的像素级脑肿瘤语义分割平台【计算机毕业设计选题推荐】
人工智能·python·深度学习·算法·毕业设计·课程设计·大数据毕业设计选题推荐